Autore Topic: NullPointerException "saltuario" in un fragment  (Letto 454 volte)

Offline ciccio

  • Utente junior
  • **
  • Post: 65
  • Respect: +7
    • Mostra profilo
  • Dispositivo Android:
    Nexus 5
  • Play Store ID:
    Francesco Cervone
  • Sistema operativo:
    Mac OS X 10.9
NullPointerException "saltuario" in un fragment
« il: 22 Gennaio 2014, 18:23:22 CET »
0
Salve a tutti.
Ho un problema con una mia app ma non riesco a capire cosa possa essere.
Ho un fragment con una ListView ed una TextView. Quando la lista è vuota la TextView diventa visibile e quando è piena invisibile.
Nel metodo onCreateView del fragment istanzio questa TextView in questo modo:
Codice (Java): [Seleziona]
mEmptyList = (TextView)rootView.findViewById(R.id.empty_list);mEmptyList è un attributo dell'oggetto fragment.
Questo oggetto viene manipolato in tanti punti, ma il problema sorge nel costruttore dell'adapter della lista, in particolare alla seguente riga:
Codice (Java): [Seleziona]
 mEmptyList.setVisibility(View.VISIBLE);Qui viene lanciato un NullPointerException. Testando l'applicazione ed usandola non mi è mai capitato questo errore. So che viene sollevata l'eccezione grazie a Google Analytics.
Un utente mi ha detto che va in errore "alcune volte" quando apre l'applicazione, ma non essendo un informatico/programmatore non mi ha saputo dire in quali casi specifici capita. E' una cosa che è successa al massimo una decina volte da quando l'app è stata rilasciata (1 mese fa).
Il costruttore dell'adapter viene chiamato in causa solo 3 volte:
  • Nel metodo onMenuItemActionCollapse del pulsante di ricerca nell'actionbar
  • Nel metodo onQueryTextChange dello stesso pulsante di prima
  • Nel metodo onCreateView del fragment, ovviamente dopo che istanzio l'oggetto mEmptyList
Qui sotto ci sono i metodi che necessari per la ricerca dell'errore.
Grazie.
Codice (Java): [Seleziona]
//onCreateView del fragment
@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_note_list, container, false);
        mRootView = rootView;
        setHasOptionsMenu(true);
        listView = (ListView) rootView.findViewById(R.id.note_list);
        mEmptyList = (TextView)rootView.findViewById(R.id.empty_list);
        mEmptyTrash = (TextView)rootView.findViewById(R.id.empty_trash);
        ...
        listView.setAdapter(new NoteListAdapter(mNotes));
        ....
        return rootView;
    }

//onCreateOptionsMenu del fragment
@Override
public void onCreateOptionsMenu(Menu menu) {
        MenuItem searchItem = menu.findItem(R.id.action_search);
        SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
        MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() {
            @Override
            public boolean onMenuItemActionExpand(MenuItem menuItem) {
                return true;
            }

            @Override
            public boolean onMenuItemActionCollapse(MenuItem menuItem) {
                listView.setAdapter(new NoteListAdapter(mNotes));
                return true;
            }
        });

        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String s) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String s) {
                if (s == null || s.equals("")) {
                    listView.setAdapter(new NoteListAdapter(mNotes));
                    return true;
                }
                LinkedList<Nota> newNotes = new LinkedList<Nota>();
                for (Nota element : mNotes) {
                    if (element.getText().toLowerCase().contains(s.toLowerCase()))
                        newNotes.add(element);
                }
                listView.setAdapter(new NoteListAdapter(newNotes));
                return false;
            }
        });
    }

//Costruttore dell'adapter
public NoteListAdapter (LinkedList list) {
            super(mActivity, R.layout.note_list_item, R.id.text1, list);
            mFilteredNotes = list;
            if (mFilteredNotes.size()==0 && mNotes.size()==0) {
                    mEmptyList.setVisibility(View.VISIBLE);
            }
            else {
                mEmptyList.setVisibility(View.GONE);
            }
        }

Offline bradipao

  • Moderatore globale
  • Utente storico
  • *****
  • Post: 4043
  • keep it simple
  • Respect: +567
    • Github
    • Google+
    • bradipao
    • Mostra profilo
  • Dispositivo Android:
    Nexus 5
  • Play Store ID:
    Bradipao
  • Sistema operativo:
    W7
Re:NullPointerException "saltuario" in un fragment
« Risposta #1 il: 22 Gennaio 2014, 18:58:37 CET »
+1
Difficile immaginare la causa vedendo spezzoni di codice e senza fare debug (anche se difficile da riprodurre).

Però posso dirti due cose che cambierei come struttura del codice:

1) Non creare un nuovo oggetto NoteListAdapter ogni volta che avviene qualcosa (apertura menu e querytextchange), perchè oggni volta che fai "new" un nuovo oggetto viene creato in memoria e quelle vecchio non viene immediatamente eliminato. Verrà eliminato più avanti nel tempo, quando il garbage collector deciderà di farlo. In più, oltre all'occupazione di memoria, c'è l'inevitabile onere della cpu che deve ogni volta perdere tempo a ricreare lo stesso oggetto. Puoi sempre modificare puntualmente la base dati e notificare all'adapter che è cambiata.

2) Poichè c'è la concreta possibilità di non riuscire a individuare il problema, conviene quanto meno evitare il crash, scrivendo la riga così: if (mEmptyList!=null) mEmptyList.setVisibility(View.VISIBLE);
NON rispondo a domande nei messaggi privati
Bradipao @ Play Store

Offline ciccio

  • Utente junior
  • **
  • Post: 65
  • Respect: +7
    • Mostra profilo
  • Dispositivo Android:
    Nexus 5
  • Play Store ID:
    Francesco Cervone
  • Sistema operativo:
    Mac OS X 10.9
Re:NullPointerException &quot;saltuario&quot; in un fragment
« Risposta #2 il: 22 Gennaio 2014, 19:41:21 CET »
0
A chi lo dici. Ho simulato tutti i casi possibili ed immaginabili ma come ho già detto non mi è mai successo.
1) Ok, hai perfettamente ragione, seguirò il tuo consiglio
2) ho già aggiunto un controllo simile :-)
Grazie

Inviato dal mio Nexus 5 utilizzando Tapatalk



Post unito: 23 Gennaio 2014, 00:20:34 CET
Sembra che abbia risolto il problema.
Fortunatamente è stato segnalato il bug e sulla console di Google Play ho potuto vedere tutto lo stacktrace.
In pratica nella mia onCreateView c'era la chiamata a setHasOptionsMenu(true) prima dell'istanziazione della TextView.
Codice (Java): [Seleziona]
        setHasOptionsMenu(true);
        mEmptyList = (TextView)rootView.findViewById(R.id.empty_list);
Solo alcune volte, quando si ritornava nell'app, questo metodo scatenava la creazione dell'actionbar e quindi dell'adapter. Per questo motivo l'app trovava l'oggetto mEmptyList nullo.
Non riesco ancora a capire però quando succede questa cosa. Forse non lo capirò mai. :D
« Ultima modifica: 23 Gennaio 2014, 00:20:34 CET da ciccio, Reason: Merged DoublePost »