在之前的问答中:
每日一问 ViewPager 这个流传广泛的写法,其实是有问题的!
我们指出了一个ViewPager 的错误写法,提到了根本原因是 Fragment存在恢复机制。
那么我们继续深入讨论一下:
- 一般情况下,我们讨论 Activity重建 Fragment恢复,都是以 Activity 旋转距离,其实还有个 case,就是进程由于内存不足被杀死,返回这个 app,Activity 也会被重建,这种情况下Fragment 也会被恢复吗(这个可以通过 app 授权一个相机权限,然后打开某个 activity,再去设置页关闭相机权限,切回 app ,就能模拟进程杀死activity 重建)?
- Fragment 恢复是真的和重建前使用的是同一个对象吗?
- 是如何做到恢复的?
更多问答 >>
-
每日一问 | 上周出现了大规模的github证书不可用的状态...但是真的是github服务器被攻击了么?
2020-04-01 21:49 -
每日一问 | 我们经常用的 String类型,你知道它最大可以放多长的字符串吗?
2020-04-08 23:58 -
2020-04-13 23:58
-
每日一问 | “必须在UI线程才能更新控件/界面” 这句人人皆知的话,100%正确吗?
2020-04-29 01:14 -
每日一问 “PathClassLoader 只能加载已安裝到系統中(即/data/app目录下)的apk文件” 严谨吗?
2020-05-05 20:46 -
每日一问 | Activity 启动动画对页面打开速度有影响吗?
2020-04-22 22:06 -
每日一问 | 自定义控件测量模式真的和 match_parent,wrap_content 一一对应吗?
2020-03-30 01:01 -
2020-03-23 23:45
-
每日一问 ViewPager 这个流传广泛的写法,其实是有问题的!
2020-03-26 00:10 -
每日一问 LifeCycle 对于 Lifecycle.Event 为啥不直接分发,而是通过 Lifecycle.State 中转?
2020-04-19 14:42
现在我们都知道,意外Destroy后重建的Activity,它原来已添加的那些 Fragment都会被恢复。
这个其实是依赖Bundle来实现的,也就是那些Fragment的相关信息,会在Activity的onSaveInstanceState
方法回调时,put
到参数outState
(Bundle)里面。outState
就是ActivityClientRecord的state
:而这些ActivityClientRecord实例,都存放在ActivityThread的
看到了那个熟悉的mActivities
里面。main
方法了吗?我们可以把这个ActivityThread类当作普通的Java程序来看待,那些Activity只不过是在这个程序里面创建的一个个对象而已,这样的话,就不难理解为什么Activity因意外重建之后,它上次保存在Bundle中的数据还在了。emmmm,那要是进程被意外杀死了呢?这时候ActivityThread都没了,它
如果能的话,很多同学首先会想到的是:可能ActivityThread在临死前把那些数据交给了生命周期更长的进程(比如系统服务)来暂时保管,重新启动时,再把数据要回来。真的是这样吗?差不多,不过不是在ActivityThread临死前转交数据,而是在Activity的mActivities
里面的ActivityClientRecord也会跟着没,像这种情况,那些数据还能恢复吗?onSaveInstanceState
和onStop
方法回调之后,就转交了,因为这个时候Activity已经变得不可见了,有被Destroy的风险。那它是怎么转交的呢?转交给谁?
先来看几张图:onSaveInstanceState
已回调,state
已经保存了那些Fragment相关信息。接着看系统服务那边:
state
最终会赋值到ActivityRecord的icicle
上的。先熟悉一下这边的环境吧:可以看到ActivityRecord的实例由TaskRecord的mActivities
来保管,而TaskRecord则存放在ActivityStack的mTaskHistory
里面(注意:ActivityRecord和TaskRecord并不是嵌套在ActivityStack中的内部类,把它画在里面只是为了更容易理解)。ActivityRecord、TaskRecord、ActivityStack分别是什么来的?ActivityRecord是用来记录对应Activity的各种信息的,如theme,启动模式、当前是否可见等等(为了排版更简洁,上图只列出来一个
icicle
),它里面还有很多管理Activity状态的相关方法;TaskRecord就是大家耳熟能详的任务栈(从上图可以看出并不真的是栈)了,它的主要职责就是管理ActivityRecord。每当Activity启动时,会先找到合适的TaskRecord(或创建新实例),然后将该Activity所对应的ActivityRecord添加到TaskRecord的
mActivities
中;ActivityStack管理着TaskRecord,当新TaskRecord被创建后,会被添加到它
mTaskHistory
里面。进一步了解:
篇幅稍长,为避免偏离主题,把这一小节放到下面一个单独的回答里了。好我们继续:
mActivityDisplays
只有一个元素(一张屏幕))。 从这个类的名字可以大概知道,它就是Activity的最顶层容器了,没错,ActivityRecord那条关系链也到此为止,即:RootActivityContainer -> ActivityDisplay -> ActivityStack -> TaskRecord -> ActivityRecord。这五个都是继承自ConfigurationContainer(上个小节有讲解),把它们归为一类不算过分。RootActivityContainer的上级:ActivityStackSupervisor,从名字上来看,它的职责应该是管理ActivityStack的,在这个类刚出现的时候(4.4(SDK19),主要是处理ActivityStack、ActivityTask、ActivityRecord相关的业务,比如移动Task/Stack位置、移除Task/Stack,获取/查找各种状态的Task/Stack/Record实例等,功能比较纯粹。但随着版本迭代,它的功能也越来越多,越来越杂,就连Google的工程师也吐槽说这个类快变成垃圾场了。。。
最外面的ActivityTaskManagerService,大家应该都会觉得挺眼熟(跟AMS的名字差不多,这个类和刚刚的RootActivityContainer一样,都是SDK 29后新增的),它其实就是分担了AMS(ActivityManagerService)的一部分工作而已(当然了,也有不少新东西,但这不是本篇回答的重点,就不多扯了,感兴趣的同学可以去比较一下它们的AIDL文件:IActivityManager.aidl(SDK 28)、IActivityManager.aidl(SDK 29)、IActivityTaskManager.aidl(SDK 29)(注意:它现在级别跟AMS一样,已经是一个系统服务了)),在此之前,各种
重点来了:(原来在ActivityManagerService中的startActivity
还有那些处理Activity的Resume、Pause、Stop、Destroy等工作都是在AMS里面调度的,现在Google把一部分方法移到了ActivityTaskManager中,可能是它觉得AMS也太臃肿了吧。activityStopped
方法,现在也搬到ActivityTaskManagerService上了。)就像前面说的那样,当Activity变得不可见时(onSaveInstanceState
和onStop
回调之后),在应用进程这边会通过ActivityTaskManagerService的activityStopped
方法,把刚刚在onSaveInstanceState
中满载了数据的Bundle对象,传到系统服务进程那边! 然后(在系统服务进程这边),会进一步将这个Bundle对象,赋值到对应ActivityRecord的icicle
上!就像这样:那么,这个数据是怎么恢复的呢?
很快就好。进一步了解ActivityRecord、TaskRecord、ActivityStack:
这三个类之间有个共同点:都是继承自ConfigurationContainer,从字面意思可以知道是配置容器,那它包括了哪些配置呢:
大概了解一下就可以了~这个ConfigurationContainer还有三个抽象方法:来看看在三个类中分别是怎么实现的:
ActivityRecord:嗯,因为它最弟弟,没有辈分比它更小的了,所以
还可以看到它持有一个TaskRecord的引用,这个就是它所属的任务栈了,所以在getChildAt
方法返回null,getChildCount
是0。getParent
方法也返回了这个对象。看下TaskRecord:很容易理解,TaskRecord管理着ActivityRecord,
getChildAt
方法直接在mActivities
里面取对应元素了,它的Parent就是ActivityStack:咦,ActivityStack的getParent方法返回的是ActivityDisplay对象,那就说明它对应的上一级是ActivityDisplay了,ActivityDisplay在那里呢?
mStacks
的List,那些ActivityStack就是存放在这里的。有同学可能会有这样疑惑:ActivityRecord对应着一个Activity实例,TaskRecord对应着一个任务栈,这两个都很容易理解为什么要用List来装(因为它们都可以同时存在多个),但ActivityStack为什么也要用List来装呢?(感觉现在一个实例都已经够用了啊,都能把系统中所有Activity和Task存起来了)这是因为【Google写成这样是为了方便以后扩展,现在暂时没有用到】还是说【在某种情况下确实需要用到多个实例】呢?如果ActivityStack会有多个,那应该怎么理解?在什么情况下会出现多个实例?其实是这样的:
虽然它名字里有Stack这个词(从名字上来看会让人觉得它跟堆栈有关),但实际上它更像是一个【工作区】,玩过Linux桌面系统的同学应该对工作区不陌生:先说相同的地方,相同的地方是它们都会为特定的Activity类型和Window显示模式来准备一些专属的Stack,比如:
画中画在日常开发中是比较少机会能接触到,但大家一定都体验过,比如微信视频通话时,右上角的那个浮动小窗,就是画中画模式。还有在YouTube客户端中按下HOME键后,正在播放的视频会以小窗形式呈现在右下角,这个小窗同样也是通过画中画模式来实现的。
自由拖动的窗口在手机上更罕见,它是这样的:就像桌面版系统一样,可以自由拖动和缩放窗口。只是因为手机屏幕小,这个选项默认是关闭的,要开启来体验下的同学可以在连接手机后,打开AS底部的Terminal窗口,把下面的命令粘贴上去然后回车等待手机重启就行了。
shell script adb shell settings put global enable_freeform_support 1 && adb shell sleep 2 && adb reboot
这条命令就是把系统设置里的enable_freeform_support
选项改成1并在2秒后重启的意思。好,回到上一个话题,除了这些专属Stack,还有一种 "通用" 的Stack,它们的ID是动态分配的,所以叫DYNAMIC_STACK。一般情况下,普通类型的Activity所属TaskRecord都是放在这些Stack里面的,当系统在为这些TaskRecord选择Stack时,会先判断能不能直接放置在callingActivity(即调用
startActivity
的那个Activity)所属Stack上,如果不能,则检查当前是否处于分屏模式,是的话就会进一步检查主屏幕Stack是否存在,存在就直接放置在副屏幕Stack上(不存在则创建)。如果没有开启分屏模式,则倒序遍历ActivityDisplay(当前所在屏幕)的mStacks
,并逐个判断Stack是否上面列出的专属Stack之一,如果不是:mStacks
,所以如果此时启动的是普通类型的Activity,就每次都会创建新Stack(因为都不符合重用的条件,不知道这是Bug还是Google有意为之))。现在我们知道了SDK【27】和【28、29】在选择Stack时的主要区别就是DYNAMIC_STACK的创建时机。如果同时开启多个普通应用,一般情况下,【28、29】版本的Stack数量是要比【27】多的。
小结:
getChildAt
方法返回TaskRecord对象,TaskRecord的getChildAt
方法返回ActivityRecord对象;先回答问题三:如何恢复?
首先是在FragmentActivity
的onSaveInstanceState
中:发现调用了
mFragments.saveAllState()
,这个mFragments
是一个FragmentController
,最后会调用到FragmentManager.saveAllState()
FragmentState
和FragmentManagerState
都是Parcelable,在里面保存信息到Parcel,这个信息最后是保存在AMS里的。然后看看恢复,在
FragmentActivity.onCreate()
里,有个参数叫savedInstanceState,是个Bundle,可以获取到保存的信息:进入到
FragmentManager.restoreAllState()
:在
FragmentState
里会重建Fragment,并且恢复保存的状态信息:继续深入看看是怎么重建的Fragment:
这里通过反射机制重新创建出了一个Fragment。
恢复的流程大概就是这样,当然省略了很多细节,感兴趣的可以自己去跟踪。然后回答一下问题二:Fragment 恢复是真的和重建前使用的是同一个对象吗?
有两种情况:设置了RetainInstance,并且进程还在:
设置了RetainInstance,Fragment会被保存到nonConfig里,重建时会直接拿出这个已有的Fragment,不会重新创建。这里可以看看之前的一个问答:Activity 都重建了,你 Fragment凭什么活着?,大佬的讲解很详细,我就不班门弄斧啦。没有设置RetainInstance,或者进程已经被kill掉了:
这种情况下,没有可以直接获得的Fragment,会通过反射创建出一个新的Fragment,这个fragment肯定和之前那个不是同一个对象。我的理解大概就是这样,如果有什么错误欢迎指出~
(另外说句题外话,洋神能审核一下后台吗,我分享了几篇觉得还不错的文章来着)好的,终于把后台文章审核完了,顺便送这几篇文章去首页啦~~感谢分享!
好的,谢谢~
保存在ActivityRecord中的数据是如何恢复的?
假设应用进程(有Activity)在后台运行(按了HOME键)时,因系统内存不足被强行结束,一段时间后重新启动,那么:既然是重新启动进程,那肯定会执行到ActivityThread的main
方法,我们就从这个main
方法开始,顺藤摸瓜:相信这段代码大家都看得想吐了吧。。。几乎每一篇【Handler机制分析】还有【为什么Looper.loop不会卡死】的文章都有它的身影,而我们这次同样也绕不开它。
可以看到在Looper初始化了之后,会创建一个ActivityThread对象,并调用它的attach
方法:刚刚调用
在if里面会调用IActivityManager的attach
方法时,第一个参数system
传的是false
(表示启动的是普通应用而非系统进程),所以会进入if里面。attachApplication
方法,并将ApplicationThread的实例传了过去,那现在就到了系统服务AMS那边咯:
现在看最后一行,那个attachApplication
里面会调用attachApplicationLocked
,这个方法里面会先回调ApplicationThread的bindApplication
(ContentProvider和Application就是在bindApplication
时初始化的)。mAtmInternal
,其实是ActivityTaskManagerService的内部类LocalService:它直接调用了RootActivityContainer的
attachApplication
方法:这个方法稍微有点复杂,它大概做了这几件事:
遍历
mActivityDisplays
集合(大多数情况下只有一个实例,对应一张屏幕),从ActivityDisplay.mStacks
中查找当前持有焦点的Stack(如果没有找到,会获取mStacks
最后一个可见且可接受焦点的Stack实例);如果能找到符合条件的Stack,会进一步将这个Stack里面的TaskRecord(任务栈)中的ActivityRecord实例放到
mTmpActivityList
中,当然了,这些ActivityRecord必须是没有finish并且可以正常显示内容的(注意!现在这些实例,都是现成的,不是新创建的!也就是说,上一次被Kill了的Application它所创建的那些Activity对应的Records也还在这里!);遍历
mTmpActivityList
,判断mTmpActivityList
中每一个ActivityRecord对象是否符合startActivity
的条件(条件为:当前没有被其他Application持有,并且用户ID和进程名都一样),符合的话会调用realStartActivityLocked
方法,把ActivityRecord传进去。注意看第三个参数andResume
,它是判断当前activity
是不是top
,这个top
就是当前任务栈顶的那个ActivityRecord实例,意思是:如果start栈顶的Activity,那就要把生命周期设置为Resumed
状态,其他的都是Paused
;好了,来看看这个
realStartActivityLocked
方法:代码有点长,但逻辑很清晰:
首先是创建了一个ClientTransaction对象,并给它添加了Callback和通过setLifecycleStateRequest
方法传进去了一个ActivityLifecycleItem对象实例。接着会调用ClientLifecycleManager的scheduleTransaction
方法,把这个ClientTransaction对象传了进去。可以看到这里还对异常做了处理:如果在scheduleTransaction
(跨进程通讯)过程中发生了异常(比如常见的TransactionTooLargeException),会立即重启目标进程,然后进行第二次尝试,如果第二次还是出了异常的话,就会强行结束掉目标进程。这里就可以解释另一个问题中的:为什么startActivity时Intent携带512K的数据,进程会突然闪退而且Logcat中看不到Log?刚刚提到的几个陌生的类:ClientTransaction、ActivityLifecycleItem、ClientLifecycleManager,是SDK 28(即 9.0)之后才有的。这里有必要先讲一下它们之间的关系以及各自的作用(特意网上搜了下,发现网上还没有讲解这一块知识的相关文章,嘿嘿~):
我们不妨把这些类代入到烟花从制造,到销售,再到燃放的各个环节中。首先是制造烟花:烟花就是ClientTransactionItem,上面说的Callback和ActivityLifecycleItem,它们都继承自抽象类ClientTransactionItem:左边蓝色的几个,就是那些Callback了(因位置有限只列举了五个比较有代表性的实现类),这些Callback的主要作用就是通知/控制ActivityThread进行相关操作(等下会详细说)。
右边的ActivityLifecycleItem依然是一个抽象类,由四个子类来实现,分别对应Activity的四个状态:Running、Paused、Stopped、Destroyed。ActivityLifecycleItem还比左边的Callback多了一样东西:TargetState(目标生命周期),等下详细讲解。Callback和ActivityLifecycleItem的父类ClientTransactionItem,继承了BaseClientRequest接口:它有三个方法,注意,除
(这个execute
之外,其余两个(preExecute
和postExecute
)都是有default
修饰(默认空实现)的,所以我们等下重点看子类实现的execute
方法就行了。execute
方法的实现,代表着烟花绽放的形状)嗯,烟花做好了之后,接下来是打包装(ClientTransaction):
可以看到这个ClientTransaction把ActivityLifecycleItem和Callback都装了进去(注意Callback是一个List,表示可以有多个Callback,不过大部分情况下只会有一个)。
好了,打包完,是时候把它运到档口销售了:
点击打开原图
烟花 (ClientTransactionItem) 打包 (ClientTransaction) 完成之后,厂家 (ClientLifecycleManager) 会把它交给中转站 (ApplicationThread) ,中转站通过有关部门 (ClientTransactionHandler) 的安全检查 (ClientTransactionItem.
哈哈,整个流程就是这样。preExecute
) 之后,就会允许在烟花店 (ActivityThread.H) 里销售。最后,顾客 (TransactionExecutor) 会从烟花店中把烟花买回来燃放 (ClientTransactionItem.execute
),放完之后,自然就把烟花壳扔掉了 (ClientTransactionItem.postExecute
)。Android 9.0之后,把ActivityThread一部分方法抽象了一下,放在ClientTransactionHandler里面(依然由ActivityThread去实现):
而那些Callback和ActivityLifecycleItem的
execute
方法的实现,其实就是直接调用上面这些抽象出来的方法而已,比如LaunchActivityItem:看吧,在创建了ActivityClientRecord对象之后就直接调用了ClientTransactionHandler(ActivityThread)的
像其他的TopResumedActivityChangeItem、WindowVisibilityItem也有对应的方法:handleLaunchActivity
方法。handleWindowVisibility
、handleTopResumedActivityChanged
ActivityLifecycleItem那四个子类就更不用说了。emmmm,前面说到,ActivityLifecycleItem会比Callbacks多携带一个东西:TargetState,这个是做什么用的呢?
其实是这样的:ActivityLifecycleItem的目的就是切换目标Activity的状态(Running、Paused、Stopped、Destroyed)。比如Activity当前状态是Running,要把它切换到Destroyed的话,就可以打包一个DestroyActivityItem。但是DestroyActivityItem的execute
方法里面只会调用ActivityThread的handleDestroyActivity
方法,众所周知,Activity从Running到Destroyed,中间还会经过onPause
和onStop
,那中间空缺的这两个回调,怎么补上呢?这时候就需要一个TargetState来告诉TransactionExecutor最终会停留在哪个状态,TransactionExecutor的cycleToPath
方法会根据这个TargetState来平滑生命周期,比如一个状态为Running的Activity,在TransactionExecutor接收到DestroyActivityItem后,调用ActivityThread的handleDestroyActivity
方法之前,会先通过TransactionExecutorHelper的getLifecyclePath
方法来计算出中间需要补上哪些回调,然后在performLifecycleSequence
方法中把这些回调补上,最后才执行DestroyActivityItem的execute
也就是ActivityThread的handleDestroyActivity
方法。好了,现在算是把整个流程搞清楚了,继续回到上面的
realStartActivityLocked
方法:现在看起来就很轻松:
它首先是给ClientTransaction打包了一个LaunchActivityItem,再打包ResumeActivityItem(假设andResume
为true,即栈顶的Activity)。LaunchActivityItem是Callback,所以会比ResumeActivityItem(ActivityLifecycleItem)先执行,那么,当这个烟花包(ClientTransaction)在应用进程那边被顾客(TransactionExecutor)拆开时,会依次做这几件事:handleLaunchActivity
(里面会回调对应Activity的onCreate
方法);handleStartActivity
会在TransactionExecutor的performLifecycleSequence
方法中先被调用;onStart
回调之后,会检测这个Activity所对应的ActivityClientRecord有没有上次保存了的Bundle,如果有的话,就会回调onRestoreInstanceState
方法,把上一次的Bundle传进去!execute
,这方法里面会调用ActivityThread.handleResumeActivity
方法;这样,Activity就正常启动了。
第3点说到,Activity的onStart
回调之后,会检测这个Activity是否有上次保存了的Bundle,那么,这个Bundle数据是怎么来的呢?上次介绍恢复流程的时候讲过,Activity的onSaveInstanceState
方法参数中的那个Bundle实例,最终会保存到系统服务进程那边的ActivityRecord.icicle
里面。现在,请继续看上面的realStartActivityLocked
方法:在创建LaunchActivityItem对象的时候,是不是把ActivityRecord的icicle
传进了LaunchActivityItem的obtain
方法中?!来看一下这个LaunchActivityItem:
当TransactionExecutor执行它的obtain
方法里面会通过setValues
方法给LaunchActivityItem的属性赋值,那个mState
对应的就是ActivityRecord.icicle
!execute
方法时,就会把这个mState
进一步传到新创建的ActivityClientRecord对象里面!这样,原来保存在系统进程的Bundle,就重新交回到应用进程了!在Activity的onStart
回调之后,这个Bundle就会传到Activity的onRestoreInstanceState
方法中!总结:
Activity的数据是怎么保存的?Activity切换到Stopped状态时,会回调onSaveInstanceState
方法,接着,这个方法参数中的Bundle对象,会通过Binder传到系统服务进程那边,保存在ActivityRecord.icicle
中。进程被Kill后,保存的数据是怎么恢复的?
因为这些数据保存在系统服务进程,所以无论应用进程怎么被Kill,那些保存了的数据也还在。当进程的Activity重新启动,RootActivityContainer会优先查找已存在的对应的ActivityRecord(如果上一次是被意外杀死,那么对应的ActivityRecord肯定还在的);找到之后,会按照正常流程启动Activity,上次保存在icicle
的那些数据,也会跟随LaunchActivityItem一起被带到应用进程中;应用进程接收到此LaunchActivityItem后,会调用ActivityThread的handleLaunchActivity
方法,这个方法需要一个ActivityClientRecord对象作为参数,所以在调用handleLaunchActivity
方法之前会创建一个ActivityClientRecord对象。刚刚传过来的icicle
,也会在此时赋值到这个新创建的ActivityClientRecord对象的state
上;handleLaunchActivity
方法里面会调用performLaunchActivity
,Activity的onCreate
也是在这方法中回调的,大家都知道,onCreate
方法也有个叫savedInstanceState
的Bundle参数,所以在回调onCreate
方法的时候,会把ActivityClientRecord的state
传进去;handleLaunchActivity
方法执行完毕之后,handleStartActivity
会被调用,在这里面会判断ActivityClientRecord的state
是否不为空(不为空则代表有数据可恢复),如果是的话,则回调对应Activity的onRestoreInstanceState
方法。整个恢复流程到此结束。我当然知道题目问的是Fragment而不是Activity,但是别忘了,这些Fragment数据同样也是保存在那个Bundle中!
接下来的Fragment恢复细节,可以看 @残页 同学的回答。emmm...模拟Activity杀死重建,可以再开发者模式里面,把“不保留活动”打开,A跳转B之后,A就会被杀死。B返回时,A就重建了~
另外,验证了,由于内存不足而杀死的Activity,并不会执行 onRetainNonConfigurationInstance(), 也就是,所有实例都不会被原地址保存下来!也就都不会是同一个对象。 ...查看更多
另外,验证了,由于内存不足而杀死的Activity,并不会执行 onRetainNonConfigurationInstance(), 也就是,所有实例都不会被原地址保存下来!也就都不会是同一个对象。
看了各位大佬的回答 有几个小问题:
1 就像缘老大 说的 activity在onSaveInstanceState跟onStop 回调之后会把数据在系统服务保留一份,那么只要是意外杀死的,重启时是不是就可以去系统服务要数据,也就是意外杀死数据不会丢?自己的数据,要在onSaveInstanceState方法中手动put
@陈小缘 意外Destroy的activity的重建与正常启动的activity有区别吗?如果没有区别系统怎么知道我们启动的activity是我们自己启动的还是杀死重建的,同一个activity是可以 ...查看更多
@陈小缘 意外Destroy的activity的重建与正常启动的activity有区别吗?如果没有区别系统怎么知道我们启动的activity是我们自己启动的还是杀死重建的,同一个activity是可以有多个实例的。一个同名的activity杀死了,我们自己启动一个同名的activity,应该是不会去系统服务拉取数据的
如果这里把Activity的onSaveInstanceState里的方法体都删掉,留下一个空方法,是不是就可以不用管那么多了?
1、通过在onSaveInstanceState进行保存当前的Fragment,在Activity恢复的时候Fragment会被恢复
2、不是同一个内存对象,但是属性值是一样的,且会被attach到Activity3、通过oncreate中的savedInstanceState来恢复Fragment;不知道对不对?