Bei Verwendung von Registerkarten mit ViewPager wird der Fehler "Java.lang.IllegalStateException-Aktivität wurde zerstört" angezeigt

110

Ich habe eine Anwendung, die aus der Verwendung von ActionBarSherlock im Registerkartenmodus besteht. Ich habe 5 Registerkarten und der Inhalt jeder Registerkarte wird mithilfe von Fragmenten behandelt. Für tab2 habe ich jedoch ein Fragment, dessen XML-Datei ein ViewPager-Element enthält, das wiederum einige Fragmentseiten enthält. Wenn ich die Anwendung zum ersten Mal starte, kann ich problemlos zwischen den Registerkarten wechseln. Wenn ich jedoch zum zweiten Mal auf Tab2 drücke, wird der oben genannte Fehler angezeigt. Die Hauptaktivität ist wie folgt:

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) 
        {

        }
    }
}

Die Fragmentklasse ohne ViewPager lautet wie folgt:

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

Die Fragmentklasse mit dem ViewPager lautet wie folgt:

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

Nach dem, was ich an verschiedenen Stellen gelesen habe (und bitte korrigieren Sie mich, wenn ich falsch liege), geschieht dies, weil der Fragmentmanager beim zweiten Durchgang versucht, die Fragmente aus der nicht mehr vorhandenen Aktivität wiederzuverwenden, wodurch der Fehler ausgegeben wird. Aber ich bin mir nicht sicher, warum dies hier passiert, da ich keine Fragmentaktivität verwende. Laut logcat befindet sich der Fehler in der onViewCreated-Methode der Tab2-Klasse in der Zeile mViewPager.setAdapter (mMyFragmentPagerAdapter). Jede Hilfe wird sehr geschätzt ... Danke.

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
quelle
Ich glaube, ich habe das Problem gefunden. Als ich die Variable mMyFragmentPagerAdapter (Klasse Tab2) über den Eclipse-Debugger betrachtete, sah ich, dass sie eine FragmentManager-Variable hatte, die beim ersten Klicken auf Tab2 ein Feld namens mActivity hatte, das auf MainActivity zeigte. Aber beim Wechsel von Tab2 zu einigen Auf der anderen Registerkarte und beim erneuten Betrachten der Aktivität hatte sie den Wert null, was möglicherweise erklärt, warum die Fehleraktivität zerstört wurde.
Yulric Sequeira

Antworten:

284

Dies scheint ein Fehler in der neu hinzugefügten Unterstützung für verschachtelte Fragmente zu sein. Grundsätzlich hat das Kind FragmentManagereinen gebrochenen inneren Zustand, wenn es von der Aktivität getrennt wird. Eine kurzfristige Problemumgehung, die es für mich behoben hat, besteht darin, zu onDetach()jedem, Fragmentden Sie anrufen, Folgendes hinzuzufügen 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
quelle
21
Wenn Sie sich die Implementierung von Fragment ansehen, werden Sie feststellen, dass beim Wechsel in den getrennten Zustand der interne Zustand zurückgesetzt wird. Der mChildFragmentManager wird jedoch nicht zurückgesetzt (dies ist ein Fehler in der aktuellen Version der Support-Bibliothek). Dies führt dazu, dass der untergeordnete Fragmentmanager nicht erneut angehängt wird, wenn das Fragment erneut angehängt wird, was zu der Ausnahme führt, die Sie gesehen haben.
Marcus Forsell Stahre
14
Du machst wohl Scherze. Ich bin froh, dass du das gepostet hast, aber gute Trauer.
Secureboot
8
Dieser Fehler wird im Android Open Source Issue Tracker verfolgt: code.google.com/p/android/issues/detail?id=42601
Kristopher Johnson
3
Eine Prise Salz, aber dies schlägt bei Verwendung der support-v4-Version oder der android.app-Version fehl. Der Fehler tritt also nicht nur in der Support-Bibliothek auf
Gbero
6
Es scheint in der Support Library Version 24.0.0 behoben zu sein. Die Methode 'performDetach ()' von 'android.support.v4.app.Fragment' führt 'mChildFragmentManager = null;' aus.
Emerson Dallagnol
13

Ich habe genau das gleiche Problem. Die einzige Problemumgehung, die ich gefunden habe, besteht darin, die Fragmente bei jedem Ändern der Registerkarten durch eine neue Instanz zu ersetzen.

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

Keine echte Lösung, aber ich habe keine Möglichkeit gefunden, die vorherige Fragmentinstanz wiederzuverwenden ...

Jekatt
quelle
1
Wow, vielen Dank ... das hat das Problem behoben. Ist es möglich, eine Erklärung zu geben, warum es funktioniert? Ich habe mir den Kopf gebrochen und versucht, den Fehler mit dem FragmentManager zu beheben.
Yulric Sequeira
1
Haben Sie Ihren Speicherbedarf analysiert? Weil diese Problemumgehung es erhöhen kann, vermute
ich
Ich weiß nicht, warum es funktioniert. Ich weiß nicht, wie es funktioniert, aber es funktioniert. lol
7

Beim Aufrufen super.onCreate()am Ende meiner Methode ist dasselbe Problem aufgetreten . Der Grund: attachActivity()wird in onCreate () von FragmentActivity aufgerufen. Beim ÜberschreibenonCreate() und Erstellen von Registerkarten versucht der Registerkartenmanager, zu einem Fragment zu wechseln, ohne dass die Aktivität an den FragmentManager angehängt ist.

Einfache Lösung: Verschieben Sie den Anruf nach super.onCreate() an den Kopf des Funktionskörpers.

Im Allgemeinen scheint es viele Gründe zu geben, warum dieses Problem auftreten kann. Dies ist nur eine andere ...

Matthias

user1050133
quelle
Vielen Dank, du hast meinen Tag gerettet! Ich hatte das gleiche Problem und konnte es nach einer Woche immer noch nicht lösen; (Ich bin also sehr froh, dass Sie diese Antwort geschrieben haben !!!
JonasPTFL
4

Ich wollte hinzufügen, dass mein Problem in einer Aktivität lag, in der ich versucht habe, ein FragmentTransactiononCreate zu erstellen, BEVOR ich anrief super.onCreate(). Ich bin gerade super.onCreate()an die Spitze der Funktion gewechselt und habe gut funktioniert.

Sgarman
quelle
2

Ich hatte diesen Fehler, weil ich LocalBroadcastManager verwendete und Folgendes tat:

unregisterReceiver(intentReloadFragmentReceiver);

anstatt:

LocalBroadcastManager.getInstance(this).unregisterReceiver(intentReloadFragmentReceiver);
Malachiasz
quelle
1
Das löst mein Problem nicht. Aber danke, es hat mir geholfen, meine Fehler bei der Verwendung von BroadcastReceivers zu korrigieren
nmxprime
Was ist, wenn Ihr Aktivitätskontext null ist? Dann wäre 'dies' nicht gültig.
IgorGanapolsky
Wann kann der Aktivitätenkontext null sein? Ich denke nie.
Malachiasz
2

Ich traf das gleiche Problem und lateron fand heraus , dass ich Anruf verpasst haben , super.onCreate( savedInstanceState );in onCreate()der FragmentActivity.

Swapnil Chaudhari
quelle
1

Ich habe den gleichen Fehler erhalten, als ich versucht habe, auf das Kind zuzugreifen, FragmentManagerbevor das Fragment vollständig initialisiert wurde (dh an die Aktivität angehängt oder zumindest onCreateView()aufgerufen). Andernfalls wird das FragmentManagermit einer nullAktivität initialisiert, die die oben genannte Ausnahme verursacht.

Ubuntudroid
quelle
1

Ich erzwinge das Fragment, das das untergeordnete Fragment enthält, in onPause auf NULL und es behebt mein Problem

fragment = null;
MobileMon
quelle
1

Ich weiß, dass dies ein alter Beitrag ist, aber die vorgeschlagenen Antworten haben an meinem Ende nicht funktioniert. Ich möchte dies hier belassen, nur für den Fall, dass jemand es nützlich findet.

Was ich getan habe ist:

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

Dann in:

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

Ich hatte dieses Problem und konnte hier keine Lösung finden. Daher möchte ich meine Lösung teilen, falls jemand anderes dieses Problem erneut hat.

Ich hatte diesen Code:

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

und löste das Problem durch Löschen der Zeile "onDestroy ();"

public void finishAction() {
  finish();
}

Der Grund, warum ich den ursprünglichen Code geschrieben habe: Ich weiß, dass die Aktivität beim Ausführen von "finish ()" "onDestroy ()" aufruft, aber ich verwende Threads und wollte sicherstellen, dass alle Threads zerstört werden, bevor ich mit der nächsten Aktivität beginne und es sieht so aus, als ob "finish ()" nicht immer unmittelbar ist. Ich muss viel "Bitmap" verarbeiten / reduzieren und große "Bitmaps" anzeigen, und ich arbeite daran, die Nutzung des Speichers in meiner App zu verbessern

Jetzt werde ich die Threads mit einer anderen Methode beenden und diese Methode von "onDestroy ()" ausführen. und wenn ich denke, ich muss alle Fäden töten.

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

Ich hatte dieses Problem und realisiert wurde , weil ich rufe setContentView(int id)zweimal in meinem Activity‚sonCreate

LukasE078
quelle
0

Dieser machte mich verrückt nach Xamarin.

Ich bin darauf mit einer ViewPager-Implementierung für TabLayout INNERHALB eines Fragments gestoßen, das selbst im DrawerLayout implementiert ist:

 - DrawerLayout
   - DrawerFragment
     - TabLayout
     - TabViewPager
       - TabViewPagerFragments

Sie müssen also den folgenden Code in Ihrem DrawerFragment implementieren . Achten Sie darauf, den richtigen FragmentManager-Pfad zu wählen. Weil Sie möglicherweise zwei verschiedene FragmentManager-Referenzen haben:

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

-> Wählen Sie die von Ihnen verwendete aus. Wenn Sie den ChildFragmentManager verwenden möchten, müssen Sie die Klassendeklaration Android.App.FragmentManager verwenden für Ihren ViewPager verwenden!

Android.Support.V4.App.FragmentManager

Implementieren Sie die folgende Methode in Ihrem "Haupt" -Fragment - in diesem Beispiel: 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 {}

Das heißt, Sie mussten den ViewPager mit Android.App.FragmentManager starten.

Implementieren Sie die folgende Methode in Ihrem "Haupt" -Fragment - in diesem Beispiel: 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+"");
    }
}
Schmetterling
quelle
0

Der Fehler wurde in der neuesten AndroidX-Version behoben. Und die berühmte Problemumgehung wird jetzt zum Absturz führen. also brauchen wir es jetzt nicht.

vipcxj
quelle