Ottenere l'errore "Java.lang.IllegalStateException Activity has been deleted" quando si utilizzano le schede con ViewPager


110

Ho un'applicazione che consiste nell'usare ActionBarSherlock in modalità tab, ho 5 tab e il contenuto di ogni tab viene gestito usando frammenti. Per tab2, tuttavia, ho un frammento il cui file xml contiene un elemento ViewPager che a sua volta ha alcune pagine di frammento. Quando avvio inizialmente l'applicazione, riesco a passare da una scheda all'altra senza problemi, ma quando premo per la seconda volta su tab2 ottengo l'errore sopra menzionato. L'attività principale è la seguente:

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 classe fragment senza ViewPager è la seguente:

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 classe fragment con ViewPager è la seguente:

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;  
        }  
   }
}

Da quello che ho letto in posti diversi (e correggetemi se sbaglio), questo accade perché il fragment manager al secondo passaggio cerca di riutilizzare i frammenti dell'attività che non esiste più dando così l'errore. Ma non sono sicuro del motivo per cui questo accada qui dato che non sto usando l'attività dei frammenti. Secondo logcat l'errore è nella classe Tab2, metodo onViewCreated sulla riga che dice mViewPager.setAdapter (mMyFragmentPagerAdapter). Qualsiasi aiuto è molto apprezzato ... Grazie.

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)

Quindi penso di aver trovato il problema. Quando ho osservato la variabile mMyFragmentPagerAdapter (classe Tab2) attraverso il debugger di eclipse, ho visto che aveva una variabile FragmentManager che quando si faceva clic su Tab2 per la prima volta aveva un campo chiamato mActivity che puntava a MainActivity.Ma passando da tab2 ad alcuni altra scheda e guardando di nuovo mActivity aveva un valore nullo, il che forse spiega perché il suo errore Activity è stato distrutto.
Yulric Sequeira

Risposte:


284

Questo sembra essere un bug nel supporto appena aggiunto per i frammenti nidificati. Fondamentalmente, il bambino FragmentManagerfinisce con uno stato interno rotto quando è staccato dall'attività. Una soluzione alternativa a breve termine che mi ha risolto è aggiungere quanto segue a onDetach()ogni Fragmentchiamata 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);
    }
}

21
Se guardi l'implementazione di Fragment, vedrai che quando passi allo stato disconnesso, ripristinerà il suo stato interno. Tuttavia, non reimposta mChildFragmentManager (questo è un bug nella versione corrente della libreria di supporto). Ciò fa sì che non ricolleghi il gestore dei frammenti figlio quando il frammento viene ricollegato, causando l'eccezione che hai visto.
Marcus Forsell Stahre

14
Mi stai prendendo in giro. Sono contento che tu abbia trovato questo post, ma buon dolore.
secureboot

8
Questo bug viene monitorato nel tracker dei problemi Android Open Source: code.google.com/p/android/issues/detail?id=42601
Kristopher Johnson

3
Un pizzico di sale, ma questo non funziona utilizzando la versione support-v4 o la versione android.app. Quindi il bug non si verifica solo nella libreria di supporto
gbero

6
Sembra corretto nella versione della libreria di supporto 24.0.0. Il metodo "performDetach ()", di "android.support.v4.app.Fragment", esegui "mChildFragmentManager = null;".
Emerson Dallagnol

13

Ho esattamente lo stesso problema. L'unica soluzione alternativa che ho trovato è sostituire i frammenti con una nuova istanza, ogni volta che le schede vengono modificate.

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

Non è una vera soluzione, ma non ho trovato un modo per riutilizzare l'istanza di frammento precedente ...


1
Wow grazie mille ... che ha risolto il problema.È possibile dare una spiegazione perché funziona? Mi stavo rompendo la testa cercando di correggere l'errore usando il FragmentManager.
Yulric Sequeira

1
Hai analizzato la tua impronta di memoria? Perché questa soluzione alternativa può aumentarlo, almeno sospetto
nmxprime

non so perché funziona .. non so come funziona ma funziona lol

7

Ho riscontrato lo stesso problema durante la chiamata super.onCreate()alla fine del mio metodo. Il motivo: attachActivity()viene chiamato in onCreate () di FragmentActivity. Durante l'override onCreate()e, ad esempio, la creazione di schede, il gestore delle schede proverà a passare a un frammento senza aver collegato l'attività al FragmentManager.

Soluzione semplice: spostare la chiamata all'inizio super.onCreate()del corpo della funzione.

In generale, sembra che ci siano molte ragioni per cui questo problema può verificarsi. Questo è solo un altro ...

Matthias


Grazie mille, mi hai salvato la giornata! Ho avuto lo stesso problema e non sono riuscito ancora a risolverlo dopo una settimana; (. Quindi sono molto felice che tu abbia scritto questa risposta !!!
JonasPTFL

4

Volevo aggiungere che il mio problema era in un'attività in cui ho provato a fare un FragmentTransactionin onCreate PRIMA di chiamare super.onCreate(). Sono appena passato super.onCreate()all'inizio della funzione e ho funzionato bene.


2

Ho riscontrato questo errore perché stavo usando LocalBroadcastManager e ho fatto:

unregisterReceiver(intentReloadFragmentReceiver);

invece di:

LocalBroadcastManager.getInstance(this).unregisterReceiver(intentReloadFragmentReceiver);

1
questo non risolve il mio problema. Ma grazie mi ha aiutato a correggere i miei errori sull'utilizzo di BroadcastReceivers
nmxprime

Cosa succede se il contesto dell'attività è nullo. Allora "questo" non sarebbe valido.
IgorGanapolsky

quando il contesto delle attività può essere nullo? Penso mai.
Malachiasz

2

Ho riscontrato lo stesso problema e in seguito ho scoperto che ho perso la chiamata a super.onCreate( savedInstanceState );in onCreate()FragmentActivity.


1

Ho ricevuto lo stesso errore durante il tentativo di accedere al bambino FragmentManagerprima che il frammento fosse completamente inizializzato (cioè collegato all'attività o almeno onCreateView()chiamato). Altrimenti, FragmentManagerviene inizializzato con un'attività che nullcausa la suddetta eccezione.


1

Forzo il frammento contenente il frammento figlio su NULL in onPause e risolve il mio problema

fragment = null;

1

So che questo è un vecchio post, ma le risposte suggerite non hanno funzionato da parte mia. Voglio lasciarlo qui nel caso qualcuno lo trovi utile.

Quello che ho fatto è:

@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();
}

Quindi in:

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

0

Ho avuto questo problema e non sono riuscito a trovare la soluzione qui, quindi voglio condividere la mia soluzione nel caso in cui qualcun altro abbia di nuovo questo problema.

Ho avuto questo codice:

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

e risolto il problema eliminando la riga "onDestroy ();"

public void finishAction() {
  finish();
}

Il motivo per cui ho scritto il codice iniziale: so che quando esegui "finish ()" l'attività chiama "onDestroy ()", ma sto usando i thread e volevo assicurarmi che tutti i thread vengano distrutti prima di iniziare l'attività successiva e sembra che "finish ()" non sia sempre immediato. Ho bisogno di elaborare / ridurre molte "bitmap" e visualizzare grandi "bitmap" e sto lavorando per migliorare l'uso della memoria nella mia app

Ora ucciderò i thread usando un metodo diverso ed eseguirò questo metodo da "onDestroy ();" e quando penso di dover uccidere tutti i thread.

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

0

Ho avuto questo problema e capito che era perché mi stava chiamando setContentView(int id)due volte nella mia Activity'sonCreate


0

Questo mi ha fatto impazzire per Xamarin.

Mi sono imbattuto in questo con un'implementazione ViewPager per TabLayout ENTRO un frammento, che a sua volta è implementato nel DrawerLayout:

 - DrawerLayout
   - DrawerFragment
     - TabLayout
     - TabViewPager
       - TabViewPagerFragments

Quindi devi implementare il seguente codice nel tuo DrawerFragment . Essere consapevoli di scegliere il percorso FragmentManager corretto. Perché potresti avere due diversi riferimenti a FragmentManager:

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

-> Scegli quello che usi. Se vuoi usare ChildFragmentManager, devi usare la dichiarazione di classe Android.App.FragmentManager per il tuo ViewPager!

Android.Support.V4.App.FragmentManager

Implementa il seguente metodo nel frammento "Main", in questo esempio: 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 {}

Ciò significa che dovevi avviare ViewPager con Android.App.FragmentManager.

Implementa il seguente metodo nel frammento "Main", in questo esempio: 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+"");
    }
}

0

Il bug è stato corretto nell'ultima versione di androidx. E la famosa soluzione alternativa ora causerà il crash. quindi non ne abbiamo bisogno ora.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.