2011-12-14 12 views
8

Ricevo questa eccezione quando ritorno all'originale ListActivity dopo aver aperto una nuova attività con il contenuto dell'elemento selezionato dall'utente. Si verifica solo su Ice Cream Sandwich.Cosa sta attivando questa istanza Exception: "java.lang.IllegalArgumentException: L'osservatore è nullo." e come potrebbe essere evitato?

Questa è la traccia:

java.lang.IllegalArgumentException: The observer is null. 
    at android.database.Observable.unregisterObserver(Observable.java:59) 
    at android.widget.BaseAdapter.unregisterDataSetObserver(BaseAdapter.java:42) 
    at android.widget.AbsListView.onDetachedFromWindow(AbsListView.java:2373) 
    at android.view.View.dispatchDetachedFromWindow(View.java:9756) 
    at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:2274) 
    at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:2272) 
    at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:2272) 
    at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:2272) 
    at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:2272) 
    at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:2272) 
    at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:2272) 
    at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:2272) 
    at android.view.ViewRootImpl.dispatchDetachedFromWindow(ViewRootImpl.java:2227) 
    at android.view.ViewRootImpl.doDie(ViewRootImpl.java:3679) 
    at android.view.ViewRootImpl.die(ViewRootImpl.java:3667) 
    at android.view.WindowManagerImpl.removeViewImmediate(WindowManagerImpl.java:320) 
    at android.view.WindowManagerImpl$CompatModeWrapper.removeViewImmediate(WindowManagerImpl.java:139) 
    at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3144) 
    at android.app.ActivityThread.access$1200(ActivityThread.java:122) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1179) 
    at android.os.Handler.dispatchMessage(Handler.java:99) 
    at android.os.Looper.loop(Looper.java:137) 
    at android.app.ActivityThread.main(ActivityThread.java:4340) 
    at java.lang.reflect.Method.invokeNative(Native Method) 
    at java.lang.reflect.Method.invoke(Method.java:511) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) 
    at dalvik.system.NativeStart.main(Native Method) 

Nessuno del mio codice è menzionato nella pila in modo che sia davvero mi sconcertante e non posso evento fare un fermo sporca:/

sto registrando un anonimo DataSetObserver solo quando l'attività è stata creata. Anche l'attività è incorporata all'interno di un ActivityGroup (che è deprecato in 4.0 ma dovrebbe ancora suportarlo, speravo).

Qualcuno ha avuto questo problema con il nuovo sistema operativo?

Grazie in anticipo.


Aggiornamento:

Ok, credo di aver trovato la fonte del problema anche se non so come risolverlo.

All'interno AbsListView.onDetachedFromWindow() Abbiamo questo:

if (mAdapter != null) { // Android code added on ICS 
    mAdapter.unregisterDataSetObserver(mDataSetObserver); 
    mDataSetObserver = null; 
} 

che, una volta l'osservatore è registrato, esso viene annullato. Il problema nasce dal fatto che, per qualche ragione, in ICS viene chiamato due volte. Penso che sia un po 'stupido per verificare i parametri nulli all'interno di un un'operazione di eliminazione come fanno nella classe Observable:

public void unregisterObserver(T observer) { // Android code 
    if (observer == null) { 
     throw new IllegalArgumentException("The observer is null."); 
    } 
    synchronized(mObservers) { 
     int index = mObservers.indexOf(observer); 
     if (index == -1) { 
      throw new IllegalStateException("Observer " + observer + " was not registered."); 
     } 
     mObservers.remove(index); 
    } 
} 

Perché non solo ignorano ¬¬ Potevano solo fare questo e avrebbe funzionato come pure (o meglio):

public void unregisterObserver(T observer) { // Android code 
    synchronized(mObservers) { 
     mObservers.remove(observer); 
    } 
} 
+0

puoi pubblicare il tuo codice attività e quando dici di tornare a fare vuoi dire premere il pulsante Indietro? – zode64

+0

Sì, tornando intendo premere il pulsante Indietro. Ecco il codice per l'attività (http://pastebin.com/68BzJcc1), ma come ho detto, non c'è il mio codice mostrato nello stack. – pablisco

+0

Ho compilato una segnalazione di bug: http://code.google.com/p/android/issues/detail?id=22946 Sebbene ci sia una soluzione: / – pablisco

risposta

11

Questo problema è stato introdotto in Android 4.0.3, e la classe Observable è stato cambiato a un'eccezione quando un osservatore è stato rilasciato più di una volta. È stato segnalato come un bug e può essere letto qui: http://code.google.com/p/android/issues/detail?id=22946.

Il modo più semplice per ovviare a questo problema è avvolgere l'adattatore sottostante ed evitare più rilasci.

@Override 
public void unregisterDataSetObserver(DataSetObserver observer) { 
    if (observer != null) { 
    super.unregisterDataSetObserver(observer); 
    } 
} 

Ma ciò non funzionerà in tutti i casi, ad es. ExpandableListView ha una scheda interna a cui non è possibile accedere. Una soluzione alternativa è quella di avvolgere ExpandableListView e rilevare l'eccezione. Questa soluzione ha funzionato per me, e non ho ancora trovato alcun effetto collaterale.

public class PatchedExpandableListView extends ExpandableListView { 

    public PatchedExpandableListView(Context context, AttributeSet attrs) { 
    super(context, attrs); 
    } 

    @Override 
    protected void onDetachedFromWindow() { 
    try { 
     super.onDetachedFromWindow(); 
    } catch(IllegalArgumentException iae) { 
     // Workaround for http://code.google.com/p/android/issues/detail?id=22751 
    } 
    } 
} 
3

ho fatto la stupido errore di pensare che nessuno dei miei corsi sono stati menzionati nella traccia, ma LoadingDataView è uno di loro. Non viene mostrato nella traccia originale ma in un'altra correlata.

Dentro quella classe c'è un ArrayAdapter anonima che era dove l'incidente sta accadendo così ho aggiunto questo come un lavoro in giro:

@Override 
public void unregisterDataSetObserver(DataSetObserver observer) { 
    if (observer != null) { 
     super.unregisterDataSetObserver(observer); 
    } 
} 

e sembra funzionare ora anche se non sono ancora sicuro perché questo il metodo è stato chiamato due volte.

Anche se, per ora in poi ho intenzione di usare frammenti per quanto posso;)

Problemi correlati