Fragment之setRetainInstance详解

对于setRetainInstance()这个方法大多数人还是比较陌生的,之前我也不太理解,只是了解个大概,就是在配置改变时,Fragment不会被重新创建,这里的配置我们就以横竖屏切换为例,这边文章将会带你从源码的角度来分析,基于support-v4-23.1.0,其他版本的原理也是一致的,相信看完之后你会对Fragment销毁时的状态的保存和重建时状态的恢复有一个更加清晰的认识。
先我们来看个简单的例子,Activity代码:

public class SecondActivity extends AppCompatActivity {
    private static final String TAG = "SecondActivity";
    private SimpleFragment fragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG, "onClick: "+getRequestedOrientation());
                if (getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                } else {
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                }
            }
        });

        FragmentManager manager = getSupportFragmentManager();
        Fragment simple_fragment = manager.findFragmentByTag("simple_fragment");
        if (simple_fragment == null) {
            fragment = new SimpleFragment();
            FragmentTransaction transaction = manager.beginTransaction();
            transaction.add(fragment,"simple_fragment").commit();
        }
        Log.d(TAG, "simple_fragment = "+simple_fragment);
    }
    
}

Fragment代码:

public class SimpleFragment extends Fragment {
    private static final String TAG = "SimpleFragment";

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        Log.d(TAG, "onAttach: ");
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate: ");
        setRetainInstance(true);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView: ");
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Log.d(TAG, "onViewCreated: ");
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Log.d(TAG, "onActivityCreated: ");
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.d(TAG, "onStart: ");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.d(TAG, "onResume: ");
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.d(TAG, "onPause: ");
    }

    @Override
    public void onStop() {
        super.onStop();
        Log.d(TAG, "onStop: ");
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.d(TAG, "onDestroyView: ");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: ");
    }

    @Override
    public void onDetach() {
        super.onDetach();
        Log.d(TAG, "onDetach: ");
    }
}

Fragment的onCreate()方法中需要,有一个setRetainInstance(true)方法,这里有个需要注意的点,那就是如果调用setRetainInstance(true),那么onCreate()就只会被调用一次。测试的时候分两种情况,一是注释掉这个方法,二是不注释掉,在这两种情况下运行并进行横竖屏切换,在Activity中查看打印SimpleFragment实例的日志,可以发现,在注释掉setRetainInstance(true)运行时,每次打印的SimpleFragment实例都是不一样的,如果不注释掉setRetainInstance(true)运行时,这就是setRetainInstance(true)的作用。下面我们就去源码中一探究竟。
先来看下setRetainInstance(true)这个方法中具体做了些什么:

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

很简单,就是对mRetainInstance 属性进行了赋值,接下来我们就该想到,在Activity销毁时,状态的保存会调用onSaveInstanceState()方法,Fragment的使用,继承的是FragmentActivity,那么我们就去FragmentActivity看看onSaveInstanceState()这个方法做了些什么东西:

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
    }

可以看到这里就是简单的调用了mFragments.saveAllState()并返回一个Parcelable对象,然后保存到了Bundle中,Activity销毁后,这个Bundle实际是ActivityManagerService给我们保存了,当下次在重新创建时再重新传回给我们,这里我们我们就不去细究了,接下来我们就去看下saveAllState()这个方法里做了些什么,这里先提一点,对于mFragments变量调用的方法最终都会调用FragmentManager中,记住了,后面就不在重复了,所以这里最终调用到的是FragmentManager中的saveAllState():

    Parcelable saveAllState() {
        // Make sure all pending operations have now been executed to get
        // our state update-to-date.
        execPendingActions();

        if (HONEYCOMB) {
            // As of Honeycomb, we save state after pausing.  Prior to that
            // it is before pausing.  With fragments this is an issue, since
            // there are many things you may do after pausing but before
            // stopping that change the fragment state.  For those older
            // devices, we will not at this point say that we have saved
            // the state, so we will allow them to continue doing fragment
            // transactions.  This retains the same semantics as Honeycomb,
            // though you do have the risk of losing the very most recent state
            // if the process is killed...  we'll live with that.
            mStateSaved = true;
        }

        if (mActive == null || mActive.size() <= 0) {
            return null;
        }
        
        // First collect all active fragments.
        int N = mActive.size();
        FragmentState[] active = new FragmentState[N];
        boolean haveFragments = false;
        for (int i=0; i<N; i++) {
            Fragment f = mActive.get(i);
            if (f != null) {
                if (f.mIndex < 0) {
                    throwException(new IllegalStateException(
                            "Failure saving state: active " + f
                            + " has cleared index: " + f.mIndex));
                }

                haveFragments = true;
                
                FragmentState fs = new FragmentState(f);
                active[i] = fs;
                
                if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
                    fs.mSavedFragmentState = saveFragmentBasicState(f);

                    if (f.mTarget != null) {
                        if (f.mTarget.mIndex < 0) {
                            throwException(new IllegalStateException(
                                    "Failure saving state: " + f
                                    + " has target not in fragment manager: " + f.mTarget));
                        }
                        if (fs.mSavedFragmentState == null) {
                            fs.mSavedFragmentState = new Bundle();
                        }
                        putFragment(fs.mSavedFragmentState,
                                FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
                        if (f.mTargetRequestCode != 0) {
                            fs.mSavedFragmentState.putInt(
                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
                                    f.mTargetRequestCode);
                        }
                    }

                } else {
                    fs.mSavedFragmentState = f.mSavedFragmentState;
                }
                
                if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
                        + fs.mSavedFragmentState);
            }
        }
        
        if (!haveFragments) {
            if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
            return null;
        }
        
        int[] added = null;
        BackStackState[] backStack = null;
        
        // Build list of currently added fragments.
        if (mAdded != null) {
            N = mAdded.size();
            if (N > 0) {
                added = new int[N];
                for (int i=0; i<N; i++) {
                    added[i] = mAdded.get(i).mIndex;
                    if (added[i] < 0) {
                        throwException(new IllegalStateException(
                                "Failure saving state: active " + mAdded.get(i)
                                + " has cleared index: " + added[i]));
                    }
                    if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
                            + ": " + mAdded.get(i));
                }
            }
        }
        
        // Now save back stack.
        if (mBackStack != null) {
            N = mBackStack.size();
            if (N > 0) {
                backStack = new BackStackState[N];
                for (int i=0; i<N; i++) {
                    backStack[i] = new BackStackState(mBackStack.get(i));
                    if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
                            + ": " + mBackStack.get(i));
                }
            }
        }
        
        FragmentManagerState fms = new FragmentManagerState();
        fms.mActive = active;
        fms.mAdded = added;
        fms.mBackStack = backStack;
        return fms;
    }

这个方法中做的主要就是保存Fragment的状态,最后全部封装到FragmentManagerState 中去并返回,这样做的目的就是当下次重新创建Activity的时候可以恢复之前创建的Fragment。
到目前为止,我们还没说到setRetainInstance()设置的mRetainInstance的作用,接下来看看,我们先从源头去跟,这个源头就是ActivityThread的performDestroyActivity()方法,在这个方法中有这样一句代码:

r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();

这个r最终是保存在ActivityManagerService中,下次重新创建时会返回给我们,接下来要做的就是去Activity中看看retainNonConfigurationInstances()这个方法是如何实现的:

    NonConfigurationInstances retainNonConfigurationInstances() {
        Object activity = onRetainNonConfigurationInstance();
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        List<Fragment> fragments = mFragments.retainNonConfig();
        ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
        if (activity == null && children == null && fragments == null && loaders == null
                && mVoiceInteractor == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.activity = activity;
        nci.children = children;
        nci.fragments = fragments;
        nci.loaders = loaders;
        if (mVoiceInteractor != null) {
            mVoiceInteractor.retainInstance();
            nci.voiceInteractor = mVoiceInteractor;
        }
        return nci;
    }

这里有一个onRetainNonConfigurationInstance()方法,这个方法在Activity是空实现,那就去它的子类,也就是FragmentActivity去看看做了些什么逻辑处理:

    @Override
    public final Object onRetainNonConfigurationInstance() {
        if (mStopped) {
            doReallyStop(true);
        }

        Object custom = onRetainCustomNonConfigurationInstance();

        List<Fragment> fragments = mFragments.retainNonConfig();
        SimpleArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();

        if (fragments == null && loaders == null && custom == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.fragments = fragments;
        nci.loaders = loaders;
        return nci;
    }

这里有个mFragments.retainNonConfig(),这里最终调用到是FragmentManager的retainNonConfig()方法,跟进去看看做了什么:

    ArrayList<Fragment> retainNonConfig() {
        ArrayList<Fragment> fragments = null;
        if (mActive != null) {
            for (int i=0; i<mActive.size(); i++) {
                Fragment f = mActive.get(i);
                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;
    }

setRetainInstance()的作用,就是在这里体现了,我们这里仔细分析下,mActive这个ArrayList集合,里面存放的是添加到FragmentManager中的Fragment,这里就是在遍历这个集合,然后取出这个集合中的每个对象,如果取出的这个对象不为null,并且f.mRetainInstance为true,就会将这个fragment添加fragments集合中,最后并返回,mRetainInstance这个属性就是通过setRetainInstance()进行设置的,忘记的可以往回看。这里返回的集合最终是会保存在ActivityManagerService中的,前面已经说过,这里在说一次,这也就是说,通过设置setRetainInstance(true),fragment实际是不会被销毁的,下次重新创建的时候会直接传过来,那是怎么传过来的呢?下面接着分析。
这里回到FragmentActivity的onCreate()中:

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        mFragments.attachHost(null /*parent*/);

        super.onCreate(savedInstanceState);

        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            mFragments.restoreLoaderNonConfig(nc.loaders);
        }
        if (savedInstanceState != null) {
            Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
            mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
        }
        mFragments.dispatchCreate();
    }

这里要注意两点:
1、getLastNonConfigurationInstance()这个方法,主要是为了之后获取Activity上次销毁时保存的fragment实例;
2、mFragments.restoreAllState()这个方法,主要是恢复上次Activity销毁时fragment的状态;
接下来就去分析这两步:
getLastNonConfigurationInstance()方法:

\\ @return Returns the object previously returned by {@link #onRetainNonConfigurationInstance()}.
    public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }

上面是文档的注释,返回的是onRetainNonConfigurationInstance()中返回的,忘记的话可以往回再看看,现在就来看看mLastNonConfigurationInstances这个属性是何时赋值的,在源码中可以看到赋值这个过程是在attach()方法中,这个方法是ActivityManagerService开启Activity时最先调用的Activity的方法,这里传过来的值也是从ActivityManagerService中从过来的,前面有说到,Activity销毁时,fragment实例以及fragment的状态都是保存到了ActivityManagerService中,正好这里又从attach()方法中传回来了,getLastNonConfigurationInstance()方法就说到这。
mFragments.restoreAllState():
前面又说道,对于mFragments调用的方法,最终调用的方法都是在FragmentManager中,所以这里就是去FragmentManager中看看他的restoreAllState(Parcelable state, List nonConfigList)是如何实现的,这里需要传进来两个参数,一个是是之前保存fragment的状态,二是通过设置setRetainInstance(true)保存起来的fragment实例:

    void restoreAllState(Parcelable state, List<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;
        
        // 这里是判断是否有fragment实例保存,有保存的话就恢复,避免后面去创建
        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];
                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(mHost.getContext().getClassLoader());
                    f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
                            FragmentManagerImpl.VIEW_STATE_TAG);
                    f.mSavedFragmentState = fs.mSavedFragmentState;
                }
            }
        }
        
        // 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) {
                Fragment f = fs.instantiate(mHost, 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);
            }
        }
        
        // Update the target of all retained fragments.
        if (nonConfig != null) {
            for (int i=0; i<nonConfig.size(); i++) {
                Fragment f = nonConfig.get(i);
                if (f.mTargetIndex >= 0) {
                    if (f.mTargetIndex < mActive.size()) {
                        f.mTarget = mActive.get(f.mTargetIndex);
                    } else {
                        Log.w(TAG, "Re-attaching retained fragment " + f
                                + " target no longer exists: " + f.mTargetIndex);
                        f.mTarget = null;
                    }
                }
            }
        }

        // Build the list of currently added fragments.
        if (fms.mAdded != null) {
            mAdded = new ArrayList<Fragment>(fms.mAdded.length);
            for (int i=0; i<fms.mAdded.length; i++) {
                Fragment f = mActive.get(fms.mAdded[i]);
                if (f == null) {
                    throwException(new IllegalStateException(
                            "No instantiated fragment for index #" + fms.mAdded[i]));
                }
                f.mAdded = true;
                if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f);
                if (mAdded.contains(f)) {
                    throw new IllegalStateException("Already added!");
                }
                mAdded.add(f);
            }
        } else {
            mAdded = null;
        }
        
        // Build the back stack.
        if (fms.mBackStack != null) {
            mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
            for (int i=0; i<fms.mBackStack.length; i++) {
                BackStackRecord bse = fms.mBackStack[i].instantiate(this);
                if (DEBUG) {
                    Log.v(TAG, "restoreAllState: back stack #" + i
                        + " (index " + bse.mIndex + "): " + bse);
                    LogWriter logw = new LogWriter(TAG);
                    PrintWriter pw = new PrintWriter(logw);
                    bse.dump("  ", pw, false);
                }
                mBackStack.add(bse);
                if (bse.mIndex >= 0) {
                    setBackStackIndex(bse.mIndex, bse);
                }
            }
        } else {
            mBackStack = null;
        }
    }

这里就是在恢复fragment,如果有保存实例的,那就不会重新创建,如果没有保存实例的那就会去重新创建,在拿到这些fragment实例后,再恢复他们之前设置的一些状态,这样,整个流程就到这了。
这样一个流程下来,不仅可以让我们对fragment的销毁和重建的整个流程有个清晰的认识,同时,对于Activity销毁时数据的保存以及恢复同样有个清晰的认识,原理是一样的。

如果其中有不明白,欢迎留言!!!

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值