causing a java.IllegalStateException error, No Activity, only when navigating to Fragment for the SECOND time

前端 未结 8 1304
感动是毒
感动是毒 2020-12-08 06:39

I am getting a very puzzling bug that I have no idea how to even begin working through.

I have a simple app with one activity, the views are implemented with Fragme

相关标签:
8条回答
  • 2020-12-08 07:26

    sadly, it's a bug of support v4, still there :(

    When you choose other Fragment via Navigation Drawer or other thing like it, the fragment which has sub-fragments is detached. So those sub-fragments' fragmentManager(getChildFragmentManager()) is no longer exist. while those fragments return, error occurred. Bomb!

    Obviously, support v4 should clean mChildFragmentManager in onDetach(), but it didn't, so we must depend on ourselves. such as following codes in the fragment which has sub-fragments:

    @Override
        public void onDetach() {
            try {
                Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
                childFragmentManager.setAccessible(true);
                childFragmentManager.set(this, null);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            super.onDetach();
        }
    

    Everything will be OK, have a good day :)

    0 讨论(0)
  • 2020-12-08 07:27

    I am stuck with the same bug using BottomNavigationView switch and replacement of fragment causing App crash. With normal speed it was working perfectly fine, if the user do fast switch it used to crash saying

    IllegalStateException No Activity

    in Logs

    Before I have

    fragmentA = new FragmentA();
    fragmentB = new FragmentB();
    fragmentC = new FragmentC();
    
    mBottomNavigationView.setOnNavigationItemSelectedListener(item -> {
            switch (item.getItemId()) {
       case R.id.fragmentA:
             replaceFragment(fragmentA);
       case R.id.fragmentB:
            repalceFragment(fragmentB);
       case R.id.fragmentC:
            repalceFragment(fragmentC);
       }
    };
    

    I think this pre-creation of fragment and using them was for replace is creating the issue, now I made it to re-create everytime. It started working fine

    mBottomNavigationView.setOnNavigationItemSelectedListener(item -> {
            switch (item.getItemId()) {
       case R.id.fragmentA:
            fragment = new FragmentA();
             replaceFragment(fragment);
       case R.id.fragmentB:
            fragment = new FragmentB();
            repalceFragment(fragment);
       case R.id.fragmentC:
            fragment = new FragmentC();
            repalceFragment(fragment);
       }
    };
    
     public void repalceFragment(Fragment fragment) {
        if (!this.isFinishing()) {
            if (!fragment.isAdded()) {
                if (!this.mDisplayedFragment.isRemoving()) {
                    getFragmentManager().beginTransaction().replace(R.id.layout_fragment_container, fragment).commit();
                    this.mDisplayedFragment = fragment;
                }
            }
        }
    }
    

    Hope its helpful for some, I did too much of defensive coding but it solved my issue of fast switching of fragments

    0 讨论(0)
  • 2020-12-08 07:29

    This appears to be a bug reported at

    https://code.google.com/p/android/issues/detail?id=42601

    The variable

    FragmentManagerImpl mChildFragmentManager;

    In Fragment.java is not set to null on detach. So the next time the fragment is loaded, the variable still points to the last parent.

    As discussed on that thread, a workaround is to reinstantiate the Fragment.

    In my case, I was switching between fragments in an ActionBar tab. The troubled Fragment has nested Fragments and was crashing the app when coming back to the file loader Fragment. So this is the work-around code:

    class MainTabsListener implements ActionBar.TabListener {
        public Fragment fragment;
        public int TabPosition;
    
        public MainTabsListener(Fragment fragment, int tab_position) {
            this.fragment = fragment;
            TabPosition = tab_position;
        }
    
        @Override
        public void onTabReselected(Tab tab, FragmentTransaction ft) {
        }
    
        @Override
        public void onTabSelected(Tab tab, FragmentTransaction ft) {
            CurrentFragment = fragment;
            CurrentTabSelectedPos = TabPosition;
    
            /**
             * This is a work-around for Issue 42601
             * https://code.google.com/p/android/issues/detail?id=42601
             * 
             * The method getChildFragmentManager() does not clear up
             * when the Fragment is detached.
             */
            if( fragment instanceof FileLoaderFragment ){
                fragment = reinstatiateFileLoaderFragment();
            }
    
            ft.replace(R.id.fragment_container, fragment);
    
        }
    
        @Override
        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
            ft.remove(fragment);
        }
    
    }
    
    0 讨论(0)
  • 2020-12-08 07:30

    Please check number 32 of this link :

    Child FragmentManager retained after detaching parent Fragment

    Steps:

    1. Download and copy this class to your project
    2. extend every Fragment with the support v4 one(best practice)
    3. use that class instead of FragmentStatePagerAdapter
    0 讨论(0)
  • 2020-12-08 07:32

    I have the same problem.

    In an activity, i have 3 bouttons to switch fragment with transaction.replace(...)

    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.layout_tablet_paneau, mLigneMessageFragment);

    One of this fragment contain a ViewPage with a custom FragmentPagerAdapter. Therefore, i must do getChildFragmentManager(), to alowed nested Fragments.

    the constructor is here:

    public LignePagerAdapter(Fragment ligneMessageTabletFragment) {
            super(ligneMessageTabletFragment.getChildFragmentManager());
        }
    

    So i have the same error: The first show of this fragment wrorks, but when i show other fragment and go back on this one, i get this exception:

    02-26 11:57:50.798: D/ACRA(776): Wait for Toast + worker ended. Kill Application ? true
    02-26 11:57:50.798: E/AndroidRuntime(776): FATAL EXCEPTION: main
    02-26 11:57:50.798: E/AndroidRuntime(776): java.lang.IllegalStateException: No activity
    02-26 11:57:50.798: E/AndroidRuntime(776):  at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1075)
    02-26 11:57:50.798: E/AndroidRuntime(776):  at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1070)
    02-26 11:57:50.798: E/AndroidRuntime(776):  at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:1861)
    02-26 11:57:50.798: E/AndroidRuntime(776):  at android.support.v4.app.Fragment.performActivityCreated(Fragment.java:1474)
    02-26 11:57:50.798: E/AndroidRuntime(776):  at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:931)
    02-26 11:57:50.798: E/AndroidRuntime(776):  at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1088)
    02-26 11:57:50.798: E/AndroidRuntime(776):  at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
    02-26 11:57:50.798: E/AndroidRuntime(776):  at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1444)
    02-26 11:57:50.798: E/AndroidRuntime(776):  at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:429)
    02-26 11:57:50.798: E/AndroidRuntime(776):  at android.os.Handler.handleCallback(Handler.java:587)
    02-26 11:57:50.798: E/AndroidRuntime(776):  at android.os.Handler.dispatchMessage(Handler.java:92)
    02-26 11:57:50.798: E/AndroidRuntime(776):  at android.os.Looper.loop(Looper.java:132)
    02-26 11:57:50.798: E/AndroidRuntime(776):  at android.app.ActivityThread.main(ActivityThread.java:4126)
    02-26 11:57:50.798: E/AndroidRuntime(776):  at java.lang.reflect.Method.invokeNative(Native Method)
    02-26 11:57:50.798: E/AndroidRuntime(776):  at java.lang.reflect.Method.invoke(Method.java:491)
    02-26 11:57:50.798: E/AndroidRuntime(776):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
    02-26 11:57:50.798: E/AndroidRuntime(776):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
    02-26 11:57:50.798: E/AndroidRuntime(776):  at dalvik.system.NativeStart.main(Native Method)
    02-26 11:57:52.818: I/dalvikvm(776): threadid=4: reacting to signal 3
    02-26 11:57:52.818: I/dalvikvm(776): Wrote stack traces to '/data/anr/traces.txt'
    
    

    So instead of put the same instance of fragment, i can re-create this so it fix the problem, but i seem not efficient.

    transaction.replace(R.id.layout_tablet_paneau, LigneMessageTabletFragment.newInstance());
    
    0 讨论(0)
  • 2020-12-08 07:34

    I followed the link in jeremyvillalobos answer (which was very helpful) that led me to this workaround.

    public class CustomFragment extends Fragment {
        private static final Field sChildFragmentManagerField;
    
        static {
            Field f = null;
            try {
                f = Fragment.class.getDeclaredField("mChildFragmentManager");
                f.setAccessible(true);
            } catch (NoSuchFieldException e) {
                Log.e(LOGTAG, "Error getting mChildFragmentManager field", e);
            }
            sChildFragmentManagerField = f;
        }
    
        @Override
        public void onDetach() {
            super.onDetach();
    
            if (sChildFragmentManagerField != null) {
                try {
                    sChildFragmentManagerField.set(this, null);
                } catch (Exception e) {
                    Log.e(LOGTAG, "Error setting mChildFragmentManager field", e);
                }
            }
        }
    
        ...
    }
    

    It works for me well, without the need to reinstantiate the fragment.

    0 讨论(0)
提交回复
热议问题