Activity的内部实例保存-Fragment.setRetainInstance

概述

Fragment 这个应该不是很陌生的东西,在这里就不做过多的介绍了。 但他有一个方法比较有意思,可以使其Fragment 的实例对象保存在Activity, 不受横竖屏切换导致重新生成对象。这就是我想介绍的Fragment 方法 setRetainInstance

作用

一般的Fragment 所在的Activity 被系统意外杀死重启后,Fragment 会在Activity onCreate 做恢复处理。
但如果在Fragment的内部有工作线程任务,例如加载资源,下载文件,加载图片等。 这些在恢复的时后都需要重新生成新的工作线程任务,原来的工作线程任务必须跟随原来的Fragment销毁而中断,不然会引起内存泄露。诚然这种运行机制可以保证Fragment能恢复相应的状态,但原来的工作任务都被取消了,而需要重新开启。
这样对整个系统都是一种浪费行为, 如果对性能要求比较高的环境下,这是很头疼的问题。
在内存不足的时候会触发android LML ,Linux oom killer ,等杀进程操作,还会有swap等交换处理,相当于消耗cpu资源和io资源。这都对系统稳定造成很大的困扰。
Fragment 方法 setRetainInstance 方法可以保证实例在整个Activity生命周期内一直存在,无需重新创建新的Fragment对象,那些依赖Fragment的工作线程也不会被舍弃,可以继续工作。

方法实现

    public void setRetainInstance(boolean retain) {
        if (retain && mParentFragment != null) {
            throw new IllegalStateException(
                    "Can't retain fragements that are nested in other fragments");
        }
        mRetainInstance = retain;
    }

这个方法还是很简单的,一个布尔值变量控制,不过需要注意的是如果传进来的参数retain为真,mParentFragment不为空则会抛出异常,大概的还以在这个异常消息也描述的很清楚, 不能保留在嵌套的fragment 。

再看看mRetainInstance这个定义

    // If set this fragment would like its instance retained across
    // configuration changes.
    boolean mRetainInstance;

这里注释给了个信息:如果设置了此片段,则希望其实例在配置更改中保留下来。

方法和变量都看完,再看mRetainInstance的相关调用位置
在这里插入图片描述
前面两个是在类FragmentState里面赋值
第一个是在FragmentState构造的时候把fragment的信息传递给FragmentState

    public FragmentState(Fragment frag) {
        mClassName = frag.getClass().getName();
        mIndex = frag.mIndex;
        mFromLayout = frag.mFromLayout;
        mFragmentId = frag.mFragmentId;
        mContainerId = frag.mContainerId;
        mTag = frag.mTag;
        mRetainInstance = frag.mRetainInstance;
        mDetached = frag.mDetached;
        mArguments = frag.mArguments;
    }

第二个是把FragmentState转换成Fragment实例

    public Fragment instantiate(Activity activity, Fragment parent) {
        //这个很关键,当mInstance不为空的时候直接返回mInstance,无需做后续的实例创建和数据恢复
        //后面在详细讲解mInstance什么时候不为空
        if (mInstance != null) {
            return mInstance;
        }
        
        if (mArguments != null) {
            mArguments.setClassLoader(activity.getClassLoader());
        }
        
        mInstance = Fragment.instantiate(activity, mClassName, mArguments);
        
        if (mSavedFragmentState != null) {
            mSavedFragmentState.setClassLoader(activity.getClassLoader());
            mInstance.mSavedFragmentState = mSavedFragmentState;
        }
        mInstance.setIndex(mIndex, parent);
        mInstance.mFromLayout = mFromLayout;
        mInstance.mRestored = true;
        mInstance.mFragmentId = mFragmentId;
        mInstance.mContainerId = mContainerId;
        mInstance.mTag = mTag;
        mInstance.mRetainInstance = mRetainInstance;
        mInstance.mDetached = mDetached;
        mInstance.mFragmentManager = activity.mFragments;
        if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
                "Instantiated fragment " + mInstance);

        return mInstance;
    }

FragmentState相当于Fragment的记录对象, 由于FragmentState是一个Parcelable序列化对象,是可以跨进程传递。至于为什么要多出一个序列化的FragmentState类,下面在详细解释。

后面的第三个到第五个是Fragment的调用
第三个就是setRetainInstance 这个方法,这个在上面已经讲过。
第四个是对应的getRetainInstance方法

    final public boolean getRetainInstance() {
        return mRetainInstance;
    }

第五个是dump方法,这个可以通过dumpsys 命令查询fragment状态

第六个,也是比较关键的一个方法
frameworks/base/core/java/android/app/FragmentManager.java
FragmentManagerImpl类的retainNonConfig方法

    ArrayList<Fragment> retainNonConfig() {
        ArrayList<Fragment> fragments = null;
        if (mActive != null) {
            for (int i=0; i<mActive.size(); i++) {
                Fragment f = mActive.get(i);
                //当fragmnet不为空并且mRetainInstance为真则添加到fragments的list集合
                if (f != null && f.mRetainInstance) {
                    if (fragments == null) {
                        fragments = new ArrayList<Fragment>();
                    }
                    fragments.add(f);
                    f.mRetaining = true;
                    f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1;
                    if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f);
                }
            }
        }
        return fragments;
    }

这里先总结一下几个调用的位置,主要有2部分
1 mRetainInstance 在FragmentState和Fragment之间互相传递
2 通过mRetainInstance判断是否添加到集合中。

这都是在干什么呢?

通过上下文 fragment 保存和恢复有两个分支机制,区别这两个分支的关键点就是mRetainInstance这个变量
分支机制1 onSaveInstanceState 的保存
通过查找FragmentState的创建在 FragmentManagerImpl类的saveAllState调用

    Parcelable saveAllState() {
        //省略...
                FragmentState fs = new FragmentState(f);
                //省略...
    }

此方法字面意思翻译为保存全部状态,可以理解保存所有fragment状态。
在看saveAllState在Activity的onSaveInstanceState内调用。

    protected void onSaveInstanceState(Bundle outState) {
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
        getApplication().dispatchActivitySaveInstanceState(this, outState);
    }

这个我们可以知道onSaveInstanceState 是直接可以保存到AMS去的。
具体onSaveInstanceState的流程机制请看 Activity的内部实例保存-onSaveInstanceState

分支机制2 retainNonConfigurationInstances 的保存
回调之前我们调用的第六个位置retainNonConfig这个方法,在Activity的retainNonConfigurationInstances方法中调用

    NonConfigurationInstances retainNonConfigurationInstances() {
        Object activity = onRetainNonConfigurationInstance();
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        ArrayList<Fragment> fragments = mFragments.retainNonConfig();
        //省略...
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.activity = activity;
        nci.children = children;
        nci.fragments = fragments;
        nci.loaders = mAllLoaderManagers;
        return nci;
    }

retainNonConfig返回的集合包含到NonConfigurationInstances对象, 这个是可以在配置切换的时候还可以保留对象的。
具体retainNonConfigurationInstances流程机制请看Activity的内部实例保存-onRetainNonConfigurationInstance

fragment的恢复是在Activity 的onCreate方法中的

    protected void onCreate(Bundle savedInstanceState) {
        //省略...
        if (savedInstanceState != null) {
            Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
            mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
                    ? mLastNonConfigurationInstances.fragments : null);
        }
        mFragments.dispatchCreate();
        getApplication().dispatchActivityCreated(this, savedInstanceState);
        mCalled = true;
    }

我们主要看这句话
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);

mLastNonConfigurationInstances这个是上次销毁传递过来的需要保留的对象。
mLastNonConfigurationInstances.fragments 这个就是之前retainNonConfigurationInstances这个方法内部 mFragments.retainNonConfig()这句话获取的fragment集合,完整的把需要保留的fragment保留了下来。
在接下来看看restoreAllState这个方法具体怎么恢复的

    void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) {
        // If there is no saved state at all, then there can not be
        // any nonConfig fragments either, so that is that.
        if (state == null) return;
        FragmentManagerState fms = (FragmentManagerState)state;
        if (fms.mActive == null) return;
        
        // First re-attach any non-config instances we are retaining back
        // to their saved state, so we don't try to instantiate them again.
        if (nonConfig != null) {
            for (int i=0; i<nonConfig.size(); i++) {
                Fragment f = nonConfig.get(i);
                if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f);
                FragmentState fs = fms.mActive[f.mIndex];
                //1 这个很关键,如果是保留集合的fragment,提前赋值给FragmentState对象的mInstance变量
                fs.mInstance = f;
                f.mSavedViewState = null;
                f.mBackStackNesting = 0;
                f.mInLayout = false;
                f.mAdded = false;
                f.mTarget = null;
                if (fs.mSavedFragmentState != null) {
                    fs.mSavedFragmentState.setClassLoader(mActivity.getClassLoader());
                    f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
                            FragmentManagerImpl.VIEW_STATE_TAG);
                }
            }
        }
        
        // Build the full list of active fragments, instantiating them from
        // their saved state.
        mActive = new ArrayList<Fragment>(fms.mActive.length);
        if (mAvailIndices != null) {
            mAvailIndices.clear();
        }
        for (int i=0; i<fms.mActive.length; i++) {
            FragmentState fs = fms.mActive[i];
            if (fs != null) {
                //2 这个之前有看过这个方法instantiate,主要是把FragmentState转换成Fragment,我们在下满展开解释说明
                Fragment f = fs.instantiate(mActivity, mParent);
                if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
                mActive.add(f);
                // Now that the fragment is instantiated (or came from being
                // retained above), clear mInstance in case we end up re-restoring
                // from this FragmentState again.
                fs.mInstance = null;
            } else {
                mActive.add(null);
                if (mAvailIndices == null) {
                    mAvailIndices = new ArrayList<Integer>();
                }
                if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i);
                mAvailIndices.add(i);
            }
        }
        //省略...
    }
    

这个方法主要有两个地方是我们比较关系的
1 fs.mInstance = f; 相当于提前把保留下来的fragment直接发给FragmentState内部变量mInstance
2 Fragment f = fs.instantiate(mActivity, mParent); FragmentState转换Fragment
我们再一次看instantiate这个方法,有两个恢复分支

    public Fragment instantiate(Activity activity, Fragment parent) {
        //1 由于之前如果有保留的fragment集合的fragment,则mInstance已经被赋值过了,不会为空,直接返回mInstance。
        if (mInstance != null) {
            return mInstance;
        }
        
        if (mArguments != null) {
            mArguments.setClassLoader(activity.getClassLoader());
        }
        //2 如果上面不是保留的framgnet 会重新创建新的fragment。
        mInstance = Fragment.instantiate(activity, mClassName, mArguments);
        //后面在做赋值操作,这里直接省略代码...
    }

恢复的两个分支也是基于mRetainInstance是否为真, 如果真则直接返回实例,如果是假则需要重新创建Fragment。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月夜持剑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值