【Android】活动的正/异常生命周期和启动模式、标志位详解
活动正常情况下生命周期的分析
-
生命周期的介绍
1.
onCreate()- 活动被创建的时候调用,写一个初始化的操作:setcontentview()加载布局,初始化活动的所需数据
2.
onRestart()- 活动由不可见变得可见时
- 一个活动执行完Onstop,又点开活动,就会执行
-
OnStart()- 当开启活动时调用,此时活动已**经可见了,**但是未显示到前台上,无法交互,用户看不见
-
onResume()- 当活动显示到前台上,开发过程中,与OnStart()不一样就是是不是在前台上;
-
onpause()- 特殊情况:用户操作很快,退到Home后又立刻进入,会由此进入到Onresume()方法中;
- 一般写一些轻量级的动画或者储存数据,但不能太耗时;
-
onStop()- 不可见;调用完onpause()立刻就会调用;
- 同样也不能做耗时操作;
- 可能会调用onrestart();
-
onDestory()- 做一些系统回收等收尾工作以及最终的资源释放;
-
线
1.
onCreate()->OnStart()->onResume()
2.onpause()->onStop()->onRestart()->OnStart()退到桌面,如果是透明主题,不会调用onstop
3.onpause()->onResume()
4. back回退会执行onDestory() -
拓展
ActivityRecord
- 一个
activity的实例,包含了这个活动的所有信息;
Task
- 包含了很多个
ActivityRecord的栈,这些活动通常属于一个app;
ActivityStack
-
是
AMS中实现调度的重要工具,在android11之后会被其他取代; -
定位
- 维护活动的任务以及返回栈
- 包含了很多
Task的一个栈
-
实例
ActivityStack (文件架) │ ├── Task A (文件夹A): [MailListActivity] (邮件列表) └── Task B (文件夹B): [BrowserHomeActivity] (浏览器主页)点开了弹窗:邮件的信息
那么就会把邮件从栈中移动到栈顶,并且打开信息界面
ActivityStack (文件架) │ ├── Task A (文件夹A): [MailListActivity, MailDetailActivity] <-- 用户看到这个 └── Task B (文件夹B): [BrowserHomeActivity]按返回键:邮件的信息->邮件列表->浏览器主页
AcitvityThread
- 是程序的入口和应用组件的控制执行器,一个进程只有一个
AcitvityThread
启动一个活动的流程:
- APPa通过
startactivity()请求通过binder到达AMS; - AMS经过效验之后,通过Binder,调用appb的进程的这个方法
ApplicationThread.scheduleLaunchActivity(); ApplicationThread(binder对象(AMS和AcitvityThread的信使))收到请求之后,会转化成Message,通过H(hanlder(AcitvityThread内部的消息分发中心))发送给主线程消息队列中- H调用这个方法
ActivityThread.handleLaunchActivity()。 - 这时
AcitvityThread开始执行创建Activity实例…Activity.onCreate();
当一个活动切换的到另外一个活动时,先执行onpause方法,才会开始创建另外一个活动;
活动异常情况下(被系统回收、系统configuration发生改变导致活动被销毁重建)
系统configuration发生改变导致活动被销毁重建
-
系统会默认销毁立刻重建手机旋转;
-
执行
onstop执行,会执行onSaveInstanceState()方法;执行onstart之后,会执行onRestoreInstanceState方法; -
通过
onSaveInstanceState()传递bundle参数,重建时Oncreate和onRestoreInstanceState都会收到bundle对象;区别就是Oncreate要判空; -
只有即将系统被销毁和有机会重新显示的时候才会调用
onSaveInstanceState(),异常情况下才会被触发; -
系统会自动做一些
view层面的数据存储和恢复:每个view都有这两个方法;
父容器委托子元素的委托思想:活动被销毁会调用
onSaveInstanceState(),然后活动委托window保存数据,window委托它的顶级容器viewgroup(DecorView),然后一一通知子元素去保存数据;
资源内存不足导致低优先级的Acitivity被杀死
-
优先级:
- 前台的活动;
- 可见但是不在前台的活动;
- 后台的活动;
-
系统会根据以上的优先级会杀死目标活动所在的进程,杀死之后可以通过以上两个方法去保存数据,没有四大组件的进程很容易被杀死;所以一些后台工作最好放在
service从而使得进程有一定的优先级,避免被杀死;
当系统结构发生改变时自定义处理方式
-
通过一个方法
android:configChandes = ""因为当发生改变时候,系统会自动销毁重建,如果设置了,就避免了销毁重建,也不会调用两个保存数据的方法; -
参数
常见的参数:
1.
locale:当设备的本地位置发生了变化,一般指系统语言;
2.orientation: 屏幕方向发生了变化;
3.keyboardHidden: 键盘的可访问性发生了变化;特殊的两个参数:
1.
screenSize:如果Minsdk和targetsdk同时小于13,当屏幕尺寸发生改变,如果不写不会导致重启,大于13的话不写就会重启;
2.smallestScreenSize::如果Minsdk和targetsdk同时小于13,当物理屏幕尺寸发生改变(外接了显示设备),如果不写不会导致重启,否则就会重启; -
当自定义之后,那么就会调用方法
onConfigurationChanded(Configuration newconfig)我们在此时做一些特殊的处理
Activity的启动模式
为啥需要启动模式
- 当活动一直出栈导致栈空,那么这个任务栈将会被系统回收;
- 如果没有启动模式,那么一个任务会被重复创建很多次,这显然不太符合我们某些特定要求的情况;
四种启动模式
Standard(标准模式)
-
如果是此模式,每次都会创建新的活动实例;
-
谁调用了此活动,那么这个活动就会出现在调用此活动的活动所在的任务栈中;
-
如果用
ApplicationContext启动Standard模式的活动会报错:-
启动该活动的不是一个活动,而是一个
context,这个context是没有任务栈的,所以会报错; -
解决方法是为活动指定标记位,使得启动的时候为它创建一个新的任务栈,同时它的模式就会发生改变;
-
SingleTop
- 如果新活动已经位于任务栈的栈顶,那么不会被重建,同时一个方法
onNewIntent()会被回调,我们可以通过这个方法的参数取出请求的信息;
SingleTask
-
主要流程:
新活动会查看有没有自己想要的任务栈,如果没有,就会创建新的任务栈,如果有,会查看自己想要的任务栈中有无自己的已经创建过的活动实例,如果有,那么旧活动之前的活动出栈,该旧活动为栈顶,同时会调用
onNewIntent()方法,如果没有,那么入栈为栈顶; -
关于前后台:
-
前后台有着不同的任务栈;
-
当后台的活动被请求,即使没有被重建,后台任务栈会被切换到前台,此时回退列表会发生改变;
举例:活动ab在前台,cd在后台,如果此时请求启动d,那么按back的结果是abcd;如果请求的是c,那么结果是abc;
-
-
关于新活动如何查看是不是自己想要的任务栈?
了解这个之前,我们需要了解一个参数
TaskAffinity
- 这个参数表示了一个活动所需任务栈的名字;
- 默认情况下,活动的任务栈的名字是应用的包名;
- 如果指定,不能为包名;
TaskAffinity与SingleTask
待启动的活动(SingleTask模式下)会运行在名字和TaskAffinity相同的任务栈中;
TaskAffinity与allowTaskReparenting
如果allowTaskReparenting为true:
- 如果应用a启动应用b中的活动,那么这个活动会从应用a的任务栈转移到应用b任务栈;
雪雪解释如下:
因为和这个活动会检查任务栈的名字,发现这个活动栈的名字和TaskAffinity不匹配,那么这个时候就会去该活动所在的应用b中去启动它,没有则创建新的任务栈,所以最终这个活动会呆在自己喜欢的任务栈中;
SingleInstance
- 是加强版的
SingleTask,除了SingleTask的特性外,还 - 当活动a启动后,那么会为这个活动去创建新的任务栈,后续的请求不会创建新的活动a的实例,直到任务栈被系统销毁;(永远只有a这一个任务)
Activity中的标志位
什么是标志位
-
为一个活动指定启动模式有两种方式,第一种是
android:launchMode = "",第二种就是通过在Intent中设置标志位来为活动指定启动模式; -
当两种指定方式都存在而且有冲突时,优先以第二种为准;
-
标志位怎么指定?
Intent intent = new Intent(); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); -
标志位的参数
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);和singleTask的使用别无二致;
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);和singleTop的使用别无二致;
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);如果启动模式设置为
standard,那么会清空该活动以上的所有活动(包括自身),新建一个活动;如果启动模式是是
singletask,那么清空,复用,调用onNewIntent方法;如果启动模式是是
singletop,若在栈顶则复用,否则新建intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);标记这个的活动不会出现在历史活动的列表中;
