Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Activity has been destroyed" sometimes when populating viewpager with fragments

I am receiving crash reports with my viewpager v22 + FragmentPagerAdapter from all versions of android 4+ and 5+. The viewpager is in a fragment and the pager adapter is using the childFragmentManager It's happening very rarely, to about 1/200 users.

java.lang.IllegalStateException: Activity has been destroyed
at android.support.v4.app.FragmentManagerImpl.void enqueueAction(java.lang.Runnable,boolean)(SourceFile:1397)
at android.support.v4.app.BackStackRecord.int commitInternal(boolean)(SourceFile:636)
at android.support.v4.app.BackStackRecord.int commitAllowingStateLoss()(SourceFile:619)
at android.support.v4.app.FragmentPagerAdapter.void finishUpdate(android.view.ViewGroup)(SourceFile:139)
at android.support.v4.view.ViewPager.void populate(int)(SourceFile:1073)
at android.support.v4.view.ViewPager.void populate()(SourceFile:919)
at android.support.v4.view.ViewPager.void onMeasure(int,int)(SourceFile:1441)
at android.view.View.measure(View.java:16574)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5140)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1406)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:697)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:590)
at android.view.View.measure(View.java:16574)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5140)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
at android.view.View.measure(View.java:16574)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:849)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:590)
at android.view.View.measure(View.java:16574)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5140)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
at android.view.View.measure(View.java:16574)
at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:719)
at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:455)
at android.view.View.measure(View.java:16574)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5140)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
at android.view.View.measure(View.java:16574)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5140)
at android.support.v7.internal.widget.ActionBarOverlayLayout.void onMeasure(int,int)(SourceFile:453)
at android.view.View.measure(View.java:16574)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5140)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
at android.view.View.measure(View.java:16574)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5140)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1406)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:697)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:590)
at android.view.View.measure(View.java:16574)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5140)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2423)
at android.view.View.measure(View.java:16574)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:1955)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1151)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1333)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1038)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5890)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
at android.view.Choreographer.doCallbacks(Choreographer.java:574)
at android.view.Choreographer.doFrame(Choreographer.java:544)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5118)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:606)
at dalvik.system.NativeStart.main(Native Method)

From the bugsense breadcrumbs, it's happening when returning to the activity, although I cannot reproduce it on any device in any memory condition or with "don't keep activities" on in the developer options. The entire app is portrait only but coming back to it from a different app in landscape doesn't cause any problems.

I am using the onDetach trick where I get "mChildFragmentManager" and null it out, and fragmentPagerAdapter trick that may complicate things, although I don't believe they're contributing to this issue.

Does anyone know how to reproduce this crash consistently or how to solve it? I don't know how the viewpager or fragment manager could still be active when the activity is destroyed. Edit: Some more code in case it helps.

  public class HomePageAdapter extends FragmentPagerAdapter implements CustomPagerSlidingTabStrip.BadgeTabProvider {

    public HomePageAdapter(FragmentManager fm)
    {
        super(fm);
    }

     @Override
      public Parcelable saveState(){
          return new Bundle();
      }
      @Override
      public void restoreState(Parcelable arg0, ClassLoader arg1) {
          //do nothing here! no call to super.restoreState(arg0, arg1);
      }
    @Override
    public Fragment getItem(int i) {
        Fragment hf;
        switch (i){

            case 0:
                hf= MyFragment.newInstance(MyFragment.MODE_FRIENDS);
                break;
            case 1:
                hf= OtherFragment.newInstance(parameter);
                break;
            default:
                return null;

        }
        hf.setRetainInstance(false);
        return hf;
    }

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

    @Override
    public String getPageTitle(int position) {
        switch (position){
            case 0:
                return "Tab 1";
            case 1:
                return "Tab 2";
            default:
                return "";

        }

    }

}

In every fragment I have

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

    } catch (NoSuchFieldException e) {
        Log.e("Hang frag " ,"no such field mchildfragmentmanager err " + e.toString());
        //throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        Log.e("Hang frag " ," illegal access mchildfragmentmanager err " + e.toString());
      //  throw new RuntimeException(e);
    }
}

EDIT: This problem no longer happens on the latest version of the support library, v4 22.2 . I also removed the saveState and restoreState methods from the adapter, but I doubt that was the issue. This was probably an arcane fragment lifecycle issue we'll never know the answer to.

like image 378
sbaar Avatar asked Apr 06 '15 21:04

sbaar


2 Answers

Because there is not any code I can not tell you exactly where the problem is but just I can address you how this problem can occur:

as you can see in the source code of fragment manager it checks the state of activity before any transaction:

public void  enqueueAction(Runnable action, boolean allowStateLoss) {
        if (!allowStateLoss) {
            checkStateLoss();
        }
        synchronized (this) {
            if (mDestroyed || mActivity == null) {
                throw new IllegalStateException("Activity has been destroyed");
            }
            if (mPendingActions == null) {
                mPendingActions = new ArrayList<Runnable>();
            }
            mPendingActions.add(action);
            if (mPendingActions.size() == 1) {
                mActivity.mHandler.removeCallbacks(mExecCommit);
                mActivity.mHandler.post(mExecCommit);
            }
        }
    }

So if your activity has been destroyed or null and the FragmentPagerAdapter tries to commit a fragment(== update itself)(as you see in the logcat: android.support.v4.app.FragmentPagerAdapter.void finishUpdate(android.view.ViewGroup)(SourceFile:139)) you may get this error.

like image 151
mmlooloo Avatar answered Oct 21 '22 02:10

mmlooloo


If this problem occurs for one person it must occur for any other device in a certain condition.

This problem might occur due to the use of getActivity() when you try to get the reference of the view pager's attached activity and that has gone to a state of rest.

If possible do not use the reference of activity by:

getActivity().somemethod.something

Instead, create a class reference of your activity within the fragment in onAttach() and use that reference to work with view pagers.

Hope this solves your problem... Cheers!!!

like image 44
Kanak Avatar answered Oct 21 '22 02:10

Kanak