博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Fragment之setRetainInstance详解
阅读量:4179 次
发布时间:2019-05-26

本文共 14536 字,大约阅读时间需要 48 分钟。

对于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
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
0) {
backStack = new BackStackState[N]; for (int i=0; i

这个方法中做的主要就是保存Fragment的状态,最后全部封装到FragmentManagerState 中去并返回,这样做的目的就是当下次重新创建Activity的时候可以恢复之前创建的Fragment。

到目前为止,我们还没说到setRetainInstance()设置的mRetainInstance的作用,接下来看看,我们先从源头去跟,这个源头就是ActivityThread的performDestroyActivity()方法,在这个方法中有这样一句代码:

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

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

NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance(); HashMap
children = onRetainNonConfigurationChildInstances(); List
fragments = mFragments.retainNonConfig(); ArrayMap
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
fragments = mFragments.retainNonConfig(); SimpleArrayMap
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
retainNonConfig() {
ArrayList
fragments = null; if (mActive != null) {
for (int i=0; i
(); } 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
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
(fms.mActive.length); if (mAvailIndices != null) {
mAvailIndices.clear(); } for (int i=0; i
(); } 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
= 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
(fms.mAdded.length); for (int i=0; i
(fms.mBackStack.length); for (int i=0; i
= 0) { setBackStackIndex(bse.mIndex, bse); } } } else { mBackStack = null; } }

这里就是在恢复fragment,如果有保存实例的,那就不会重新创建,如果没有保存实例的那就会去重新创建,在拿到这些fragment实例后,再恢复他们之前设置的一些状态,这样,整个流程就到这了。

这样一个流程下来,不仅可以让我们对fragment的销毁和重建的整个流程有个清晰的认识,同时,对于Activity销毁时数据的保存以及恢复同样有个清晰的认识,原理是一样的。

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

转载地址:http://tkhai.baihongyu.com/

你可能感兴趣的文章
SpringBoot | 第二章:配置多环境以及上传文件
查看>>
Spring Data JPA |自定义非实体类的映射
查看>>
SpringBoot | 常用注解记录
查看>>
JavaBean对象转换EntityUtils工具类
查看>>
Maven常用命令
查看>>
SpringBoot | 运行报错,无法加载oracle连接驱动
查看>>
为什么阿里巴巴禁止在 foreach 循环里进行元素的 remove/add 操作
查看>>
AWS EC2如何从普通用户切换为root用户
查看>>
click方法不生效的
查看>>
mysql排行榜并列与不并列
查看>>
SpringBoot | Mybatis申明为Mapper文件
查看>>
JPA主键生成策略
查看>>
byte数组和InputStream的相互转换
查看>>
InputStream,InputStreamReader和Reader之间的区别与关系
查看>>
Java中System.arraycopy方法的使用
查看>>
tk.mybatis的使用记录
查看>>
遍历获取目录下的所有文件
查看>>
从指定服务器路径下载文件
查看>>
EasyExcel读取和写入java model数据
查看>>
《C编译原理》共享库的动态加载和静态加载
查看>>