2011-12-14 21 views
8

Obtengo esta excepción cuando regreso al original ListActivity después de abrir una nueva actividad con el contenido del elemento seleccionado por el usuario. Solo ocurre en Ice Cream Sandwich.¿Qué está desencadenando esta instancia de excepción: "java.lang.IllegalArgumentException: el observador es nulo". y ¿cómo podría evitarse?

Esta es la traza:

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) 

Ninguno de mi código es mencionado en la pila por lo que realmente me ha desconcertante y no puedo evento de hacer una captura sucia:/

estoy registrando una anónimo DataSetObserver solo cuando se crea la actividad. Además, la actividad está incrustada dentro de un ActivityGroup (que está en desuso en 4.0 pero aún debería soportarlo, esperaba).

¿Alguien ha tenido este problema con el nuevo sistema operativo?

Gracias de antemano.


Actualización:

Ok, que he encontrado el origen del problema aunque no estoy seguro de cómo resolverlo.

Dentro AbsListView.onDetachedFromWindow() tenemos esto:

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

que, una vez que el observador no está registrada, que se anuló. El problema es que, por alguna razón, en ICS se llama dos veces. Creo que es un poco tonto para comprobar si hay parámetros nulos dentro de una operación de eliminación como lo hacen en la clase 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); 
    } 
} 

¿Por qué no simplemente lo ignoran ¬¬ Ellos sólo podían hacer esto y trabajarían así (o mejor):

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

¿Puedes publicar tu código de actividad y cuando dices volver significa ¿presionar el botón Atrás? – zode64

+0

Sí, al regresar me refiero a presionar el botón Atrás. Aquí está el código para la actividad (http://pastebin.com/68BzJcc1), pero como menciono, no se muestra mi código en la pila. – pablisco

+0

He llenado un informe de error: http://code.google.com/p/android/issues/detail?id=22946 Aunque debe haber una solución: / – pablisco

Respuesta

11

Este problema se introdujo en Android 4.0.3, y la clase observable fue cambiado a lanzar una excepción cuando un observador fue lanzado más de una vez. Se ha informado como un error y se puede leer aquí: http://code.google.com/p/android/issues/detail?id=22946.

La forma más fácil de evitar este problema es ajustar el adaptador subyacente y evitar varias versiones.

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

Pero eso no funcionará en todos los casos, p. ExpandableListView tiene un adaptador interno al que no se puede acceder. Una solución alternativa aquí es ajustar ExpandableListView y capturar la excepción. Esta solución funcionó para mí, y todavía no he encontrado ningún efecto secundario.

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

hice la tonta error de pensar que ninguno de mis clases fueron mencionados en la traza, pero LoadingDataView es uno de ellos. No se muestra en el trazado original, pero sí en otro relacionado.

Dentro de esa clase no es una ArrayAdapter anónima que era donde el incidente está ocurriendo así que añadió esta foto como el trabajo en torno a:

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

Y parece que funciona ahora, aunque todavía no estoy seguro de por qué esto método fue llamado dos veces.

Aunque, por ahora en adelante voy a utilizar fragmentos tanto como pueda;)

Cuestiones relacionadas