在上一问,我们问了:
假设我这个 Activity 内部有一个异步线程正在下载东西,并不支持暂停恢复,断开就需要重新下载,但是 Activity 本身支持横竖屏切换显示。
在 Activity 旋转重建的前提下,如果让这个异步线程继续下载,不会受到牵连呢?
有同学回答使用:
Fragment.setRetainInstance(true)
好了,那么问题来了:
- 设置了setRetainInstance的 Fragment,当 Activity 发生旋转重建时,Fragment 对象会发生变化吗?
- 如果不会,Activity 都销毁重建了,Fragment 怎么活下来的?
- 如果活下来,那么肯定脱离于 Activity 存储在某个声明生命周期更长的对象中,具体的源码是如何实现的?
更多问答 >>
-
每日一问 ViewPager 嵌套,“老子”怎么就没拦住你?
2019-12-29 23:52 -
每日一问 很多书籍上写:“事件分发只有一次 ACTION_DOWN,一次 ACTION_UP”严谨吗?
2020-01-07 00:08 -
每日一问 为什么 Dialog 默认弹出后 Activity 就无法响应用户事件了?
2020-01-15 19:24 -
每日一问 据说很多 app 在 2019 年最后一周都出现了日期上的 bug ?
2020-01-14 20:33 -
每日一问 系统帮我们做了网络安全校验,那么还需要我们手动去做吗?
2020-01-14 20:33 -
2019-12-20 00:08
-
2019-12-15 23:55
-
每日一问 Android 签名机制 v1 v2 v3 , 卧槽都 v3 了?
2019-12-09 23:52 -
2019-12-04 00:18
-
每日一问 | Activity启动流程中,大部分都是用Binder通讯,为啥跟Zygote通信的时候要用socket呢?
2019-11-30 20:49
源码分析 (看之前建议先看看昨晚的回答,会对总体的流程有个大致的了解)
首先看看调用setRetainInstance(true)后会发生什么:
在赋值给
当FragmentManager添加Fragment的时候,会调用一个mRetainInstance
之后,还把mRetainInstanceChangedWhileDetached
设为true。makeActive
方法:可以看到,如果Fragment的
mRetainInstanceChangedWhileDetached
和mRetainInstance
都为true的话,就会调用addRetainedFragment
方法:嗯!它会用到我们昨晚说的那个FragmentManagerViewModel的
addRetainedFragment
方法,看看它里面是怎样的:没错了,就像昨晚说的那样:设置
接着来看看FragmentManagerImpl的FragmentManagerViewModel是怎样保存到ComponentActivity(AppCompatActivity也是间接继承它的)的ViewModelStore里面的:其实很简单,在FragmentManagerImpl初始化的时候就已经put进去了:来看看它初始化的方法:mRetainInstance
为true的Fragment,会添加到FragmentManagerViewModel的mRetainedFragments
中。可以看到,
这个mNonConfig
初始化时有三个分支,会走哪一个呢?attachController
方法,会在FragmentActivity的onCreate
方法回调时第一时间调用,而FragmentActivity继承自ComponentActivity,ComponentActivity是实现了ViewModelStoreOwner的!所以这里的分支,会走第二个。第二个分支,它会调用FragmentManagerViewModel的getInstance
方法,并将一个ViewModelStore传进去,可以看到这个ViewModelStore是通过host的getViewModelStore
方法来获取的,而此时的host,就是FragmentActivity!来看看它的getViewModelStore
方法是怎么样的:它会先尝试从
getLastNonConfigurationInstance
方法里获取,如果没有就创建新的对象并返回。好,回到刚刚说的第二个分支里,当它调用了FragmentManagerViewModel的
看看:getInstance
方法(ViewModelStore传进去)之后,发生了什么呢?很简单,就是先创建一个ViewModelProvider对象,创建ViewModelProvider对象时,会把参数ViewModelStore(也就是ComponentActivity中的ViewModelStore对象)和一个FACTORY传进去,这个FACTORY,它里面有个create方法,里面会返回一个新的FragmentManagerViewModel对象。
创建了ViewModelProvider对象之后,会直接return(注意这里的return是return到mNonConfig
的赋值语句中的) ViewModelProvider的get
方法的返回值,也就是说,mNonConfig
最终引用的对象,就是ViewModelProvider的get方法返回的对象。那现在来看看ViewModelProvider这个类:
emmm,它的get方法,会先调用mFactory的
create
方法来获取一个ViewModel对象(注意!这个mFactory就是我们刚刚说的那个,也就是create方法会返回FragmentManagerViewModel对象了),接着!会把这个ViewModelput
到mViewModelStore(还记得这个mViewModelStore吗?不记得就看一下上面分析分支那里)里面去!这也就对应了我们昨晚说的:在初始化时就已经把自身put进去了。好,现在知道了设置
那么接下来,就看看这个ViewModelStore它是怎么传递到新创建的Activity中的:在Activity里面有个mRetainInstance
为true的Fragment最终会间接保存到ComponentActivity的ViewModelStore里面。onRetainNonConfigurationInstance
,但它是一个空方法,这个方法在ComponentActivity中被重写了:就像昨晚说的那样,在这个方法内,会把ViewModelStore交给新创建的NonConfigurationInstances来保存,并返回了这个NonConfigurationInstances对象。
那么,这个方法会在哪里调用呢?Activity的retainNonConfigurationInstances
方法中:嗯,在
ActivityThread的retainNonConfigurationInstances
方法里,又多套了一层,那这个方法又会在哪些调用呢?performDestroyActivity
方法!看吧,这样就把设置
当执行完Destroy任务之后,接着会调用mRetainInstance
为true的Fragment间接保存在ActivityClientRecord里了!performLaunchActivity
方法,并将刚刚performDestroyActivity
方法返回的那个ActivityClientRecord对象传进去了:看!在创建了新的Activity实例之后,又把刚刚存在ActivityClientRecord的那个
lastNonConfigurationInstances
取出来,并传进新Activity的attach
方法里了:emmm,在这个方法中,把
传到新Activity之后,发生了什么?现在,请看回上面 "lastNonConfigurationInstances
赋值给Activity的成员变量mLastNonConfigurationInstances
了,也就是说,原来在旧Activity中那些Fragment实例,现在已经传到了新的Activity里了。mNonConfig
初始化时有三个分支" 那一段!!!懒得滑回去看的话,这里就直接说了:还记不记得mNonConfig
是一个ViewModel?还记不记得,它会在创建时put到ComponentActivity的ViewModelStore里?还记不记得,它里面有一个Fragment的集合,用来保存那些设置了RetainInstance为true的Fragment?嗯!当FragmentActivity的onCreate回调时,会尝试从新Activity的
mLastNonConfigurationInstances
中拿出旧Activity的那个mNonConfig
实例!并赋值给新Activity的mNonConfig
!恢复了
mNonConfig
实例后,接下来会发生什么?恢复了
会把mNonConfig
实例后,会回调到FragmentManagerImpl的restoreSaveState
方法,这个方法里面会做些什么呢?mNonConfig
里面的那些Fragment拿出来,放到FragmentManagerImpl的mActive
中(mActive
是一个HashMap<String, Fragment> )。接着,FragmentActivity会调用FragmentManagerImpl的dispatchCreate
方法,把那些旧Fragment重新激活!那么,从 “保存” 到 “恢复” 的整个流程,到这里就结束了。牛逼克拉斯
把Fragment的RetainInstance设置为true,当Activity旋转后,Fragment对象会发生变化吗?
对象不会变,还是原来的实例。但是在Activity Destroy时,这个Fragment却不会回调onDestroy
,而是直接回调onDetach
。Activity作为Fragment的寄主,都销毁重建了,Fragment是怎么做到不重新创建对象的呢?
总的来说,也是通过onSaveInstanceState
方法来保存实例的,当Activity重新创建的时候,会把存在Bundle里面的实例重新取出来(如果有的话),并回调对应的生命周期方法。详细一点,是这样的(整个流程有点复杂,可能要多看几次才能理解。。。):
在ComponentActivity(AppCompatActivity也是间接继承它的)中,有个成员变量ViewModelStore,这个ViewModelStore里面有个HashMap<String, ViewModel>,没错,就是用来保存ViewModel对象实例的;在FragmentManager(Impl)中,有个成员变量FragmentManagerViewModel(继承自ViewModel),叫
刚刚说的FragmentManagerViewModel,它里面有个HashSet<Fragment>,叫mNonConfig
,它会在ComponentActivity的onCreate
方法回调时初始化,注意!在它初始化的时候,还会顺便把它自身put到ComponentActivity的ViewModelStore里面的HashMap中!mRetainedFragments
,当我们调用Fragment的setRetainInstance
方法时,如果传的是true的话,那么这个Fragment会顺便扔到前面说的mRetainedFragments
中(注意!这个时候,那些设置了RetainInstance为true的Fragment,已经是间接存在于ComponentActivity的ViewModelStore中的了!);旋转屏幕的时候:
在ActivityThreadperformDestroyActivity
时(Activity的onDestroy也是这个时候回调),ComponentActivity会把它的ViewModelStore(也就是间接保存了那些Fragment的那个实例),交给新创建的NonConfigurationInstances对象保管,而这个NonConfigurationInstances对象,最终会赋值给ActivityClientRecord的lastNonConfigurationInstances!接着,ActivityThread会调用handleLaunchActivity
方法,重新通过反射创建Activity实例,并调用Activity的attach
方法(注意!调用attach
方法时会把刚刚存在ActivityClientRecord中的lastNonConfigurationInstances
(里面间接保存了那些Fragment)也传进去!);在Activity的attach
方法里面,会把参数lastNonConfigurationInstances
赋值给Activity的成员变量mLastNonConfigurationInstances
!当Activity的onCreate方法被回调时,在FragmentActivity(AppCompatActivity也是间接继承它的)中的实现,第一时间会初始化FragmentManager;在初始化FragmentManager过程中,会顺便初始化刚刚说到的成员变量mNonConfig
(也就间接保存那些Fragment的ViewModel);在初始化刚刚说到的成员变量mNonConfig
时,会调用ComponentActivity(FragmentActivity继承自它)的getViewModelStore
方法来获取上面说到的ViewModelStore对象!getViewModelStore
方法是怎么返回的呢?它会先尝试从Activity中的mLastNonConfigurationInstances
(刚刚说attach
方法时有介绍)里面获取!获取不到才创建新对象。这时候肯定能获取到了,那么现在的mNonConfig
对象,就已经是旋转屏幕前的对象了!还没完!接着,FragmentActivity会间接回调FragmentManager的restoreSaveState
方法,这个方法是做什么的呢?就是会把保存在mNonConfig
中的HashSet<Fragment>里面那些Fragment(就是前面说的那些setRetainInstance为true的Fragment),全部重新添加到FragmentManager中!!!源码分析明天再写,好困啊(我太难了)。建议用手机来看,排版更清晰一点
let me 优化一下展示
其实如果将这个命题改为 viewmodel 如何自动保存和恢复(config 变化时重建)的话,可能分析起来要简单些,然后再 扩展fragment 比较好理解,
查看FragmentManager源码 -> saveNonConfig -> FragmentManagerNonConfig -> 存到了ActivityThread, 和ViewModel一样。
简单来说RatainedFragment被缓存到一个NonConfigStateInstance对象里,此对象在Activity发生重建(Relauncher)的时候,被ActivityThread中的activityClientRecord持有,以便下次Activity恢复时,在activity.attach()中,传入NonConfigStateInstance.RetainedFragment。
有了解过,但不深入。
1.Fragment对象不会发生变化。2.Fragment的生命周期是依附于Activity的,activity旋转后本来应该执行的生命周期是onDestroyView()->onDestroy()->onDetach(),但如果设置了setRetaineInstance(true),在onDestroyView()中会进行判断,最后就会跳过onDestroy()直接执行onDetach()(这就导致了界面消失了,但对象还存在),Fragment重组的时候会跳过执行onCreate(),但其他的生命周期函数还是会执行的,这就达到了上一个问题的作用。3.这我就不清楚了,我去看下小缘大佬的详细解释,我只知道设置了setRetaineInstance(true)后,Fragment是不会加入backstack中的,然后它是应该游离态的,因为长时间没有容器去承载它,它也是会被系统回收掉的。