web-dev-qa-db-fra.com

Obtention de l'erreur "L'activité Java.lang.IllegalStateException a été détruite" lors de l'utilisation d'onglets avec ViewPager

J'ai une application qui consiste à utiliser ActionBarSherlock en mode onglet. J'ai 5 onglets et le contenu de chaque onglet est géré à l'aide de fragments. Pour tab2 cependant, j'ai un fragment dont le fichier XML contient un élément ViewPager qui contient à son tour des pages de fragment. Lorsque je lance l'application pour la première fois, je peux basculer entre les onglets sans problème, mais lorsque j'appuie sur tab2 pour la deuxième fois, l'erreur mentionnée ci-dessus est détectée. L'activité principale est la suivante:

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 de fragments sans ViewPager est la suivante:

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 de fragment avec ViewPager est la suivante:

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

D'après ce que j'ai lu à différents endroits (et corrigez-moi si je me trompe), cela se produit parce que le gestionnaire de fragments du second passage tente de réutiliser les fragments de l'activité qui n'existe plus, ce qui donne l'erreur. Mais je ne sais pas pourquoi cela se produit ici puisque je n'utilise pas l'activité de fragment. Selon logcat, l'erreur est dans la classe Tab2, méthode onViewCreated sur la ligne indiquant mViewPager.setAdapter (mMyFragmentPagerAdapter). Toute aide est grandement appréciée ... Merci.

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)
109
user1950599

Cela semble être un bug dans le support nouvellement ajouté pour les fragments imbriqués. Fondamentalement, l'enfant FragmentManager se retrouve avec un état interne cassé lorsqu'il est détaché de l'activité. Une solution de contournement à court terme qui l'a corrigé pour moi consiste à ajouter ce qui suit à onDetach() sur chaque Fragment que vous appelez getChildFragmentManager() sur:

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

J'ai exactement le même problème. La seule solution que j'ai trouvée consiste à remplacer les fragments par une nouvelle instance chaque fois que les onglets sont modifiés.

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

Pas une vraie solution, mais je n'ai pas trouvé de moyen de réutiliser l'instance de fragment précédente ...

13
jekatt

J'ai rencontré le même problème en appelant super.onCreate() à la fin de ma méthode. La raison: attachActivity() est appelée dans onCreate () de FragmentActivity. Lors de la substitution de onCreate() et, par exemple, de la création d'onglets, le gestionnaire d'onglets essaiera de basculer vers un fragment sans que l'activité ne soit liée au gestionnaire FragmentManager.

Solution simple: déplacez l'appel vers super.onCreate() vers la tête du corps de la fonction.

En général, il semble y avoir une foule de raisons pour lesquelles ce problème peut se produire. Ceci est juste un autre ...

Matthias

7
user1050133

Je voulais ajouter que mon problème était lié à une activité dans laquelle je tentais de créer une FragmentTransaction dans onCreate AVANT d'avoir appelé super.onCreate(). Je viens de déplacer super.onCreate() vers le haut de la fonction et tout a bien fonctionné.

4
sgarman

J'ai eu cette erreur parce que j'utilisais LocalBroadcastManager et j'ai:

unregisterReceiver(intentReloadFragmentReceiver);

au lieu de:

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

J'ai rencontré le même problème et j'ai découvert par la suite que j'avais manqué l'appel de super.onCreate( savedInstanceState ); dans onCreate() de FragmentActivity.

2
Swapnil Chaudhari

Je force le fragment contenant le fragment enfant à NULL dans onPause et corrige mon problème

fragment = null;
1
MobileMon

J'ai eu la même erreur en essayant d'accéder à l'enfant FragmentManager avant que le fragment ne soit complètement initialisé (c'est-à-dire attaché à l'activité ou au moins onCreateView() appelé). Sinon, la FragmentManager est initialisée avec une activité null provoquant l'exception susmentionnée.

1
ubuntudroid

Je sais que ceci est un ancien post, mais les réponses suggérées n'ont pas fonctionné de mon côté. Je veux laisser ceci ici juste au cas où quelqu'un le trouverait utile.

Ce que j'ai fait c'est:

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

Puis dans:

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

J'ai eu ce problème et je ne pouvais pas trouver la solution ici, donc je veux partager ma solution au cas où quelqu'un d'autre aurait à nouveau ce problème.

J'ai eu ce code:

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

et a résolu le problème en supprimant la ligne "onDestroy ();"

public void finishAction() {
  finish();
}

La raison pour laquelle j'ai écrit le code initial: je sais que lorsque vous exécutez "finish ()", l'activité appelle "onDestroy ()", mais j'utilise des threads et je voulais m'assurer que tous les threads sont détruits avant de commencer l'activité suivante. et on dirait que "finish ()" n’est pas toujours immédiat. Je dois traiter/réduire beaucoup de "bitmap" et afficher de gros "bitmaps" et je travaille à améliorer l'utilisation de la mémoire dans mon application.

Maintenant, je vais tuer les threads en utilisant une méthode différente et je vais exécuter cette méthode à partir de "onDestroy ();" et quand je pense que je dois tuer tous les fils.

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

Celui-ci m'a rendu fou pour Xamarin.

Je me suis heurté à cela avec une implémentation ViewPager pour TabLayout WITHIN dans un fragment, qui est elle-même implémentée dans DrawerLayout:

 - DrawerLayout
   - DrawerFragment
     - TabLayout
     - TabViewPager
       - TabViewPagerFragments

Vous devez donc implémenter le code suivant dans votre DrawerFragment. Sachez que vous choisissez le bon chemin FragmentManager. Parce que vous pourriez avoir deux références FragmentManager différentes:

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

-> Choisissez celui que vous utilisez. Si vous souhaitez utiliser ChildFragmentManager, vous devez utiliser la déclaration de classe Android.App.FragmentManager pour votre ViewPager!

Android.Support.V4.App.FragmentManager

Implémentez la méthode suivante dans votre fragment "principal" - dans cet exemple: 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 {}

Cela signifie que vous deviez initialiser ViewPager avec Android.App.FragmentManager.

Implémentez la méthode suivante dans votre fragment "principal" - dans cet exemple: 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
Lepidopteron

J'ai eu ce problème et j'ai réalisé que c'était parce que j'appelais setContentView(int id) deux fois dans mon Activity 'onCreate

0
LukasE078