Obteniendo el error "Java.lang.IllegalStateException La actividad ha sido destruida" al usar pestañas con ViewPager

110

Tengo una aplicación que consiste en usar ActionBarSherlock en modo pestaña, tengo 5 pestañas y el contenido de cada pestaña se maneja mediante fragmentos. Sin embargo, para tab2, tengo un fragmento cuyo archivo xml contiene un elemento ViewPager que a su vez tiene algunas páginas de fragmentos. Cuando inicio la aplicación por primera vez, puedo cambiar entre pestañas sin problema, pero cuando presiono la pestaña 2 por segunda vez, aparece el error mencionado anteriormente. La actividad principal es la siguiente:

public class MainActivity extends SherlockFragmentActivity
{
    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ActionBar actionBar = getSupportActionBar();

        ActionBar.Tab tab1 = actionBar.newTab().setText("Tab1");
        ActionBar.Tab tab3 = actionBar.newTab().setText("Tab3");
        ActionBar.Tab tab2 = actionBar.newTab().setText("Tab2");
        ActionBar.Tab tab4 = actionBar.newTab().setText("Tab4");
        ActionBar.Tab tab5 = actionBar.newTab().setText("Tab5");

        Fragment fragment1 = new Tab1();
        Fragment fragment3 = new Tab3();
        Fragment fragment2 = new Tab2();
        Fragment fragment5 = new Tab5();
        Fragment fragment4 = new Tab4();

        tab1.setTabListener(new MyTabListener(fragment1));
        tab3.setTabListener(new MyTabListener(fragment3));
        tab2.setTabListener(new MyTabListener(fragment2));
        tab5.setTabListener(new MyTabListener(fragment5));
        tab4.setTabListener(new MyTabListener(fragment4));

        actionBar.addTab(tab1);
        actionBar.addTab(tab2);
        actionBar.addTab(tab3);
        actionBar.addTab(tab4);
        actionBar.addTab(tab5); 

        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    }

    class MyTabListener implements ActionBar.TabListener
    {
        Fragment fragment;

        public MyTabListener(Fragment fragment)
        {
            this.fragment = fragment;
        }

        @Override
        public void onTabSelected(com.actionbarsherlock.app.ActionBar.Tab tab,FragmentTransaction ft) 
        {
            ft.replace(R.id.fragment_container,fragment);
        }

        @Override
        public void onTabUnselected(com.actionbarsherlock.app.ActionBar.Tab tab,FragmentTransaction ft) 
        {

        }

        @Override
        public void onTabReselected(com.actionbarsherlock.app.ActionBar.Tab tab,FragmentTransaction ft) 
        {

        }
    }
}

La clase de fragmento sin ViewPager es la siguiente:

public class Tab1 extends Fragment 
{
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)
    {
        return inflater.inflate(R.layout.activity_tab1, container, false);
    }
}

La clase de fragmento con ViewPager es la siguiente:

public class Tab2 extends Fragment 
{
    ViewPager mViewPager;
    private MyFragmentPagerAdapter mMyFragmentPagerAdapter;  
    private static int NUMBER_OF_PAGES = 5;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)
    {
        View view =  inflater.inflate(R.layout.activity_tab2, container, false); 
        return view;
    }

    @Override
    public void onViewCreated(View view,Bundle savedInstanceState)
    {
        super.onViewCreated(view, savedInstanceState);
        mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
        mMyFragmentPagerAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());  
        mViewPager.setAdapter(mMyFragmentPagerAdapter);  
    }

    private static class MyFragmentPagerAdapter extends FragmentPagerAdapter 
    {    
        public MyFragmentPagerAdapter(FragmentManager fm) 
        {  
             super(fm);  
        }  

        @Override  
        public Fragment getItem(int index) 
        {  
             return PageFragment.newInstance("My Message " + index);
        }  

        @Override  
        public int getCount() 
        {  
             return NUMBER_OF_PAGES;  
        }  
   }
}

Por lo que he leído en diferentes lugares (y corríjame si me equivoco), esto sucede porque el administrador de fragmentos en la segunda pasada intenta reutilizar los fragmentos de la actividad que ya no existe, lo que genera el error. Pero no estoy seguro de por qué sucede esto aquí, ya que no estoy usando actividad de fragmentos. Según logcat, el error está en la clase Tab2, método onViewCreated en la línea que dice mViewPager.setAdapter (mMyFragmentPagerAdapter). Cualquier ayuda es muy apreciada, gracias.

03-04 12:01:05.468: E/AndroidRuntime(2474): FATAL EXCEPTION: main
03-04 12:01:05.468: E/AndroidRuntime(2474): java.lang.IllegalStateException: Activity has been destroyed
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1342)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.BackStackRecord.commitAllowingStateLoss(BackStackRecord.java:578)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:139)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.view.ViewPager.populate(ViewPager.java:1011)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.view.ViewPager.populate(ViewPager.java:880)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.view.ViewPager.setAdapter(ViewPager.java:433)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at com.example.tabs.Tab2.onViewCreated(Tab2.java:31)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:925)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1088)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1444)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:429)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.os.Handler.handleCallback(Handler.java:587)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.os.Handler.dispatchMessage(Handler.java:92)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.os.Looper.loop(Looper.java:123)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.app.ActivityThread.main(ActivityThread.java:3687)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at java.lang.reflect.Method.invokeNative(Native Method)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at java.lang.reflect.Method.invoke(Method.java:507)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at dalvik.system.NativeStart.main(Native Method)
Yulric Sequeira
fuente
Así que creo que he encontrado el problema. Al mirar la variable mMyFragmentPagerAdapter (clase Tab2) a través del depurador de eclipse, vi que tenía una variable FragmentManager que al hacer clic en Tab2 por primera vez tenía un campo llamado mActivity que apuntaba a MainActivity. Pero al cambiar de tab2 a algunos otra pestaña y mirando de nuevo mActivity tenía un valor de nulo, lo que quizás explica por qué está dando el error Activity ha sido destruido.
Yulric Sequeira

Respuestas:

284

Esto parece ser un error en el soporte recién agregado para fragmentos anidados. Básicamente, el niño FragmentManagertermina con un estado interno roto cuando se separa de la actividad. Una solución a corto plazo que me solucionó es agregar lo siguiente a onDetach()todos los Fragmentque llame getChildFragmentManager():

@Override
public void onDetach() {
    super.onDetach();

    try {
        Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
        childFragmentManager.setAccessible(true);
        childFragmentManager.set(this, null);

    } catch (NoSuchFieldException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}
Marcus Forsell Stahre
fuente
21
Si observa la implementación de Fragment, verá que al pasar al estado separado, restablecerá su estado interno. Sin embargo, no restablece mChildFragmentManager (este es un error en la versión actual de la biblioteca de soporte). Esto hace que no vuelva a adjuntar el administrador de fragmentos secundarios cuando se vuelve a adjuntar el fragmento, lo que provoca la excepción que vio.
Marcus Forsell Stahre
14
Tienes que estar bromeando. Me alegro de que hayas publicado esto, pero Dios mío.
Secureboot
8
Este error se está rastreando en el rastreador de problemas de código abierto de Android: code.google.com/p/android/issues/detail?id=42601
Kristopher Johnson
3
Una pizca de sal, pero esto está fallando con la versión support-v4 o la versión android.app. Entonces, el error no solo ocurre en la biblioteca de soporte
gbero
6
Parece arreglado en la biblioteca de soporte versión 24.0.0. El método 'performDetach ()', de 'android.support.v4.app.Fragment', hace 'mChildFragmentManager = null;'.
Emerson Dallagnol
13

Tengo exactamente el mismo problema. La única solución que he encontrado es reemplazar los fragmentos por una nueva instancia, cada vez que se cambian las pestañas.

ft.replace(R.id.fragment_container, Fragment.instantiate(PlayerMainActivity.this, fragment.getClass().getName()));

No es una solución real, pero no he encontrado una manera de reutilizar la instancia del fragmento anterior ...

jekatt
fuente
1
Wow muchas gracias ... eso solucionó el problema. ¿Es posible dar una explicación de por qué funciona? Me estaba rompiendo la cabeza tratando de corregir el error usando FragmentManager.
Yulric Sequeira
1
¿Ha analizado su huella de memoria? Debido a que esta solución puede aumentarlo, al menos sospecho
nmxprime
no sé por qué funciona ... no sé cómo funciona pero funciona jajaja
7

Encontré el mismo problema al llamar super.onCreate()al final de mi método. El motivo: attachActivity()se llama en onCreate () de FragmentActivity. Al anular onCreate()y, por ejemplo, crear pestañas, el administrador de pestañas intentará cambiar a un fragmento sin tener la actividad adjunta al FragmentManager.

Solución simple: mueva la llamada a super.onCreate()al encabezado del cuerpo de la función.

En general, parece que hay muchas razones por las que este problema puede ocurrir. Este es solo otro ...

Matías

usuario1050133
fuente
¡Muchas gracias, me salvaste el día! Tuve el mismo problema y todavía no pude resolverlo después de una semana; (. ¡
Estoy
4

Quería agregar que mi problema estaba en una actividad en la que traté de hacer un FragmentTransactiononCreate ANTES de llamar super.onCreate(). Acabo de pasar super.onCreate()a la parte superior de la función y funcionó bien.

sgarman
fuente
2

Tuve este error porque estaba usando LocalBroadcastManager y lo hice:

unregisterReceiver(intentReloadFragmentReceiver);

en vez de:

LocalBroadcastManager.getInstance(this).unregisterReceiver(intentReloadFragmentReceiver);
Malaquiasz
fuente
1
esto no resuelve mi problema. Pero gracias, me ayudó a corregir mis errores al usar BroadcastReceivers
nmxprime
¿Qué pasa si el contexto de tu actividad es nulo? Entonces 'esto' no sería válido.
IgorGanapolsky
cuando el contexto de actividades puede ser nulo? Creo que nunca.
Malachiasz
2

Me encontré con el mismo problema y lateron descubrí que, me he perdido llamada a super.onCreate( savedInstanceState );en onCreate()de FragmentActivity.

Swapnil Chaudhari
fuente
1

Recibí el mismo error al intentar acceder al niño FragmentManagerantes de que el fragmento se inicializara por completo (es decir, se adjuntara a la Actividad o al menos se onCreateView()llamara). De lo contrario, FragmentManagerse inicializa con una nullactividad que causa la excepción antes mencionada.

ubuntudroid
fuente
1

Obligo el fragmento que contiene el fragmento secundario a NULL en onPause y soluciona mi problema

fragment = null;
MobileMon
fuente
1

Sé que esta es una publicación antigua, pero las respuestas sugeridas no funcionaron por mi parte. Quiero dejar esto aquí en caso de que alguien lo encuentre útil.

Lo que hice fue:

@Override
public void onResume() {
    super.onResume();
    // add all fragments
    FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
    for(Fragment fragment : fragmentPages){
        String tag = fragment.getClass().getSimpleName();
        fragmentTransaction.add(R.id.contentPanel, fragment, tag);
        if(fragmentPages.indexOf(fragment) != currentPosition){
            fragmentTransaction.hide(fragment);
        } else {
            lastTag = tag;
        }
    }
    fragmentTransaction.commit();
}

Luego en:

@Override
public void onPause() {
    super.onPause();
    // remove all attached fragments
    for(Fragment fragment: fragmentPages){
        getChildFragmentManager().beginTransaction().remove(fragment).commit();
    }
}
Caja cuadrada
fuente
0

Tuve este problema y no pude encontrar la solución aquí, así que quiero compartir mi solución en caso de que alguien más tenga este problema nuevamente.

Tenía este código:

public void finishAction() {
  onDestroy();
  finish();
}

y resolvió el problema eliminando la línea "onDestroy ();"

public void finishAction() {
  finish();
}

La razón por la que escribí el código inicial: sé que cuando ejecutas "finish ()", la actividad llama a "onDestroy ()", pero estoy usando subprocesos y quería asegurarme de que todos los subprocesos se destruyen antes de comenzar la siguiente actividad. , y parece que "fin ()" no siempre es inmediato. Necesito procesar / reducir una gran cantidad de "mapas de bits" y mostrar "mapas de bits" grandes y estoy trabajando para mejorar el uso de la memoria en mi aplicación

Ahora mataré los hilos usando un método diferente y ejecutaré este método desde "onDestroy ();" y cuando creo que necesito matar todos los hilos.

public void finishAction() {
  onDestroyThreads();
  finish();
}
user713059
fuente
0

Tenía este problema y se dio cuenta que era porque yo estaba llamando setContentView(int id)dos veces en mis Activity'sonCreate

LukasE078
fuente
0

Este me volvió loco por Xamarin.

Me encontré con esto con una implementación de ViewPager para TabLayout WITHIN a Fragment, que a su vez se implementa en DrawerLayout:

 - DrawerLayout
   - DrawerFragment
     - TabLayout
     - TabViewPager
       - TabViewPagerFragments

Así que tienes que implementar el siguiente código en tu DrawerFragment . Tenga en cuenta que debe elegir el FragmentManager-Path correcto. Porque es posible que tenga dos referencias de FragmentManager diferentes:

  1. Android.Support.V4.App.FragmentManager
  2. Android.App.FragmentManager

-> Elija el que usa. Si desea hacer uso de ChildFragmentManager, debe usar la declaración de clase Android.App.FragmentManager para su ViewPager.

Android.Support.V4.App.FragmentManager

Implemente el siguiente método en su fragmento "principal", en este ejemplo: DrawerFragment

public override void OnDetach() {
    base.OnDetach();
    try {
        Fragment x = this;
        var classRefProp = typeof(Fragment).GetProperty("class_ref", BindingFlags.NonPublic | BindingFlags.Static);
        IntPtr classRef = (IntPtr)classRefProp.GetValue(x);
        var field = JNIEnv.GetFieldID(classRef, "mChildFragmentManager", "Landroid/support/v4/app/FragmentManagerImpl;");
        JNIEnv.SetField(base.Handle, field, IntPtr.Zero);
    }
    catch (Exception e) {
        Log.Debug("Error", e+"");
    }
}

Android.App.FragmentManager

public class TabViewPager: Android.Support.V13.App.FragmentPagerAdapter {}

Eso significa que tuvo que iniciar ViewPager con Android.App.FragmentManager.

Implemente el siguiente método en su fragmento "principal", en este ejemplo: DrawerFragment

public override void OnDetach() {
    base.OnDetach();
    try {
        Fragment x = this;
        var classRefProp = typeof(Fragment).GetProperty("class_ref", BindingFlags.NonPublic | BindingFlags.Static);
        IntPtr classRef = (IntPtr)classRefProp.GetValue(x);
        var field = JNIEnv.GetFieldID(classRef, "mChildFragmentManager", "Landroid/app/FragmentManagerImpl;");
        JNIEnv.SetField(base.Handle, field, IntPtr.Zero);
    }
    catch (Exception e) {
        Log.Debug("Error", e+"");
    }
}
Lepidopteron
fuente
0

El error se ha corregido en la última versión de androidx. Y la famosa solución alternativa provocará un bloqueo ahora. así que no lo necesitamos ahora.

vipcxj
fuente