1、典型状况下的生命周期
1)onCreate:Activity正在创建。
2)onRestart:Activity正在启动。【Activity从onStop返回时会调用该方法】
3)onStart:Activity正在被启动。Activity已经可见,但是还没有出现在前台,还无法和用户交互。
4)onResume:Activity已经可见,且出现在前台,可以和用户交互。
5)onPause:Activity正在停止。正常情况下,紧接着onStop会调用。
特殊情况下,此时快速再回到当前Activity,那么onResume会被调用。
这里不能做耗时操作,会影响新Activity的显示,onPause必须执行完,新Activity的onResume 才会执行。
6)onStop:Activity即将停止,此时回到当前Activity,会执行 onRestart -> onStart -> onResume
7)onDestroy:Activity即将被销毁,可以做一些回收和最终的资源释放。
注意事项:
1)打开新Activity时,onPause -> onStop ,如果新Activity采用透明主题,那么当前Activity不会调onStop
2)用户回到原Activity,onRestart -> onStart -> onResume
3)onCreate和onDestroy配对;onStart和onStop配对;onResume和onPause配对
4)onStart和onStop 是从Activity是否可见来回调的。
onResume和onPause 是从Activity是否位于前台来回调的。
5)onPause一定会在新Activity onResume前调用,所以不能在onPause中执行重量级操作,避免阻塞。
2、异常情况下的生命周期
[1]资源相关的系统配置发生变化导致Activity被杀死并重新创建
1)系统配置改变(横竖屏切换),Activity会被销毁
onPause -> onSaveInstanceState -> onStop -> onDestory -> onCreate -> onStart -> onRestoreInstanceState
保存数据: onSaveInstanceState 在onStop之前调用,和onPause没用既定时序关系,可能在onPause前也可能在之后调用。
恢复数据: onRestoreInstanceState 在onStart之后调用。
Activity、Fragment、View都有onSaveInstanceState、onRestoreInstanceState用于保存和恢复数据。
2)关于保存和恢复View层次结构
Activity意外终止时,Activity会调用 onSaveInstanceState去保存数据,然后Activity会委托Window去保存数据,
接着Window再委托它上面的顶层容器去保存数据。顶层容器是一个ViewGroup,一般来说它很可能是DecorView.
最后顶层容器再一一通知它的子元素保存数据。恢复也是类似。
3)系统只在Activity异常终止时才会调用 onSaveInstanceState 和 onRestoreInstanceState 来存储和恢复数据。
[2]资源内存不足导致低优先级的Activity被杀死
1)数据存储和恢复过程与系统配置变化导致Activity被杀死并重新创建一致
2)Activity优先级高低
a、前台Activity,正在和用户交互的Activity,优先级最高
b、可见单非前台Activity,比如Activity中弹出了一个对话框,导致Activity可见但是位于后台,
无法和用户交互
c、后台Activity,已经被暂停的Activity,比如执行了onStop,优先级最低。
3)系统内存不足时,系统会按照上述优先级杀死目标Activity所在的进程,
并在后续通过 onSaveInstanceState 和 onRestoreInstanceState 来存储和恢复数据。
如果一个进程中没有四大组件在执行,那么该进程将很容易被杀死。因此,一些后台工作不适合脱离四大组件而肚子运行在后台中。
应该将后台工作放入Service中从而保证进程有一定的优先级,就不会轻易被系统杀死。
4)给Activity指定 configChanges 属性,避免系统配置改变后,Activity被杀死重建
android:configChanges="orientation|screenSize|smallestScreenSize"
在Manifest中对应Activity加了此配置后,选择屏幕不会重新创建Activity,也不会触发onSaveInstanceState和onRestoreInstanceState
而是调用了Activity的 onConfigurationChanged 方法。
configChanges配置项 含义
mcc SIM卡唯一标识IMSI(国际移动用户识别码)中的国家代码,三位数字组成,中国为460。
标识mcc代码发生了改变
mnc SIM卡唯一表示IMSI中的运营商代码,两位数字组成,中国移动TD系统为00,联通01,电信03
标识mnc发生改变
locale 设备的本地位置发生了改变,一般指切换了系统语言
touchscreen 触摸屏发生了改变,(这个正常情况下无法发生,可忽略)
keyboard 键盘类型发生改变,比如用户使用了外插键盘
keyboardHidden 键盘的可访问行发生了改变,比如用户调出了键盘
navigation 系统导航方式改变,比如采用了轨迹球导航,(难发生,可忽略)
screenLayout 屏幕布局改变,可能是用户激活了另外一个显示设备
fontScale 系统字体所发比例发生了改变,比如用户选择了一个新字号
uiMode 用户界面模式改变,比如是否开启了夜间模式(API8添加)
orientation 屏幕方向改变,比如旋转了手机屏幕
screenSize 屏幕尺寸信息改变。旋转屏幕时,屏幕尺寸改变。
当minSdkVersion和targetSdkVersion都低于13时,此选项不会导致Activity重启,否则会导致Activity重启。
smallestScreenSize 设备的物理屏幕尺寸改变。与屏幕方向无关。仅仅表示实际物理屏幕尺寸改变时发生。
比如,用户切换到外部显示设备。
当minSdkVersion和targetSdkVersion都低于13时,此选项不会导致Activity重启,否则会导致Activity重启。
layoutDirection 布局方向改变。API17新增。
3、Activity的启动模式及应用场景
任务栈分为前台任务栈和后台任务栈,后台任务栈中的Activity位于暂停状态,用户可以通过切换将后台任务栈再次调到前台。
1)standard:标准模式,系统的默认模式。每次启动一个Activity都会重新创建要给新的实例。
谁启动这个Activity,这个Activity就运行在启动它的那个Activity所在的栈中。
用ApplicationContext启动standard模式的Activity会报错,因为standard模式的Activity默认会进入启动它的Activity所属的任务栈,
但是非Activity类型的Context没有任务栈。
此时要给待启动Activity指定 FLAG_ACTIVITY_NEW_TASK 标记位,这样启动时会为它创建一个新的任务栈,
这时候待启动Activity实际上是以 singleTask 模式启动的。
2)singleTop:栈顶复用模式。
如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建。同时它的 onNewIntent 会被会回调。
onCreate、onStart不会被系统调用,因为它没有发生改变。
如果新Activity已存在但是不在栈顶,会创建新的Activity。
3)singleTask:栈内复用模式。只要Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,系统会回调 onNewIntent。
【singleTask有clearTop的效果】,启动的Activity实例在栈中非栈顶位置时,会导致该Activity实例上面的Activity全部出栈。
当一个singleTask模式的Activity请求启动后,系统会寻找是否存在该Activity想要的任务栈,如果不存在则创建一个任务栈,
然后创建Activity放入栈中。如果存在该栈,则判断栈中是否存在该Activity实例,如果存在则把Activity移到栈顶并调用onNewIntent
如果Activity实例不存在,则创建实例并放入栈中。
4)singleInstance:单例模式,是一种加强的singleTask模式,具有singleTask模式的所有特性,
且此模式的Activity只能单独的位于一个任务栈。除非该任务栈被系统销毁,否则永远不会创建新的Activity。
5)
假设有2个任务栈,前台任务栈中有AB,后台任务栈有CD,后台栈栈顶为D。假设CD的启动模式都是singleTask。
此时前台栈的栈顶B启动D,那么整个后台任务栈都会被移动到前台栈中,此时前台栈中为ABCD。
如果B启动C,那么C会被移到前台栈,D会因为 C 的 singleTask 的 clearTop 效果而出栈,此时前台栈中为ABC。
6)TaskAffinity:带有包名分隔符"."的字符串,标识一个Activity所需要的任务栈的名字,默认情况,所有Activity任务栈的名字为应用包名。
TaskAffinity属性主要和singleTask启动模式、allowTaskReparenting属性配对使用。
TaskAffinity和singleTask配对使用时,该Activity会运行在名字和TaskAffinity相同的任务栈中。
TaskAffinity和allowTaskReparenting配对使用:
两个应用A和B,A启动B的一个 Activity_C ,如果Activity_C的 allowTaskReparenting属性为true,那么应用B被启动后,
Activity_C会直接从应用A 的任务栈转移到应用B 的任务栈中。
解释:应用A 启动了 Activity_C,这时候Activity_C 运行在应用A 的任务栈中,但是Activity_C是属于应用B的
当B被启动后,创建了B应用的任务栈,这时候系统发现Activity_C 原本想要的任务栈已经创建,
会把 Activity_C 从从应用A 的任务栈移到 应用B 的任务栈。
7)为Activity指定启动模式
第一,Manifest中 android:launchMode="singleTask"
第二,intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
第二种偶现及高于第一种,两种同时存在时,以第二种方式为准。
8)Activity的Flags
FLAG_ACTIVITY_NEW_TASK -singleTask
FLAG_ACTIVITY_SINGLE_TOP -singleTop
FLAG_ACTIVITY_CLEAR_TOP -启动该标记位的Activity,启动时,在同一个任务栈中所有在它上面的Activity都要出栈
FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_CLEAR_TOP:如果被启动的Activity已经存在,系统会调用它的onNewIntent。
FLAG_ACTIVITY_CLEAR_TOP:如果被启动的Activity已经存在,并且是standard模式启动,
那么存在的Activity连同它之上的Activity都要出栈,系统会创建新的Activity实例放入栈顶。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:该标记的Activity不会出现在历史Activity列表中,等同 android:excludeFromRecents="true"
什么是recents?通俗的讲就是android的多任务栈,它可以看到我们最近使用过的应用,通过它可以快速应用切换。
4、IntentFilter的匹配规则
<intent-filter>
<action android:name="com.ryg.charpter"/>
<category android:name="com.ryg.category"/>
<data android:mimeType="text/plain"/>
</intent-filter>
Activity隐式调用需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息,如果不匹配则无法启动该Activity
IntentFilter中的过滤信息有action、category、data。
一个Activity可以有多个intent-filter,一个intent-filter可以有多个action、category、data。
一个Intent只要能够匹配任何一组intent-filter就可以成功启动对应的activity
1)action 的匹配规则
字符串匹配,Intent中必须有一个action并且和过滤规则中的任何一个action相同即可。
2)category 的匹配规则
不含有 android.intent.DEFAULT 这个category的Activity是无法接收隐式Intent的。
字符串匹配,Intent中可以没有category
但是如果Intent中有category,那么所有category都必须和过滤规则中的其中一个category相同
startActivity/startActivityForResult中会默认为Intent加上android.intent.category.DEFAULT 这个category
同时,为了activity能够接收隐式调用,必须在intent-filter中指定 android.intent.category.DEFAULT 这个category
所以Intent中没有手动添加category时也是可以匹配上的。
3)data 的匹配规则
<data
android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mimeType="string"
/>
如果intent-filter中定义了data,那么Intent中必须要定义可匹配的data。
data有mineType 和 URI 组成。
mimeType指媒体类型,比如image/jpeg、audio/mpeg4-generic等,表示图片、文本、视频等。
URI结构:<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
Scheme:URI的模式,比如http、file、content,如果没有指定scheme,整个URI其他参数无效,URI也无效。
Host:URI的主机名,比如www.baidu.com,如果host为指定,整个URI的其他参数无效,URI也无效。
Port:URI中端口号,比如80,仅当URI中指定了scheme和host参数时port参数才有意义
Path、pathPattern、pathPrefix:表述路径信息。
path表示完整路径
pathPattern 表示完整路径信息,但是它里面可以包含通配符“*”,“*”表示0个或多个任意字符
如果想表示真实的*字符串,那么要写成“\\*”,\要写成“\\\\”
pathPrefix表示路径前缀信息
匹配规则:
a、scheme有时候有默认值
<intent-filter>
<data android:mimeType="image/*">
</intent-filter>
指定媒体类型为所有类型的图片,Intent中mineType属性必须为"image/*"才能匹配
此时过滤规则虽然没有指定URI,但是有默认值,URI默认值为content和file
Intent中URI部分的scheme必须为content或者file才能匹配。
b、如果要为Intent指定完整的data,必须调用setDataAndType方法,不能先调用setData再调用setType
这两个方法彼此会清除对方的值。
<intent-filter>
<data android:mimeType="video/mpeg"
android:scheme="http"/>
</intent-filter>
匹配写法:intent.setDataAndType(Uri.parse("http://abc", "video/mpeg");
4)通过隐式调用启动一个Activity时,最好判断是否有Activity能够匹配我们的隐式Intent,避免出现ActivityNotFoundException
方法一:PackageManager.resolveActivity(Intent intent, int flags)
方法二:intent.resolveActivity(PackageManager pm, int flags)
方法三:PackageManager.queryIntentActivities(Intent, int flags)
如果找不到匹配的Activity会返回null。
int flags参数:使用 Intent.MATCH_DEFAULT_ONLY,表示仅仅匹配在intent-filter中声明了
<category android:name="android.intent.DEFAULT"/> 的Activity。
使用该标记位的意义:只要上述两个方法不返回null,那么startActivity一定可以成功。
5)重要的action和category
<acton android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
二者表明一个入口Activity,并且出现在系统的应用列表中,二者缺一不可。
5、进程和应用生命周期
https://blog.csdn.net/u010577768/article/details/81087452
1、Activity启动过程
1)ActivityManagerProxy 是AMS在app进程的Binder代理, ActivityManagerNative 是对该Binder代理的操作工具类
IApplicationThread 是应用启动时调用AMS.attachApplication(IApplicationThread thread)传到AMS进程的app进程的Binder代理。
ApplicationThreadNative 是 IApplicationThread 的实现, ApplicationThread 是对 ApplicationThreadNative 的封装
2) 启动activity会先把要启动的activity信息通过 ActivityManagerProxy 传给AMS
3) AMS会对要启动的activity检查,包括检查activity是否在Manifest中注册
4) AMS检查完后会通过app进程的Binder代理 ApplicationThreadNative 把activity传回到app进程
5) ApplicationThread 中会通过ActivityThread.mH这个Handler把要启动的activity传到app进程的主线程
6) Handler.dispatchMessage(msg)中如果mCallback非空,会走mCallback.handleMessage(msg)
7) app主线程中通过 Instrumentation来真正启动activity
2、启动插件Activity要做的事
1)绕过AMS对启动activity是否在Manifest中注册的检查
Hook掉ActivityManagerProxy,修改内部一些方法,startActivity(),
把启动的activity替换成提前在Manifest中注册的代理activity,这样AMS检查的实际上就是这个代理activity;
2)AMS检查完后,把启动的activity还原回目标activity
方法一:Hook掉ActivityThread.mH,给mH这个handler的mCallback传值,这样会走mCallback中逻辑,
在mCallback中把代理activity替换回要启动的activity
方法二:Hook掉ActivityThread.mInstrumentation,替换成自定义的代理Instrumentation,
代理Instrumentation.newActivity()中把代理activity替换成要启动的activity
推荐阅读:
《Android开发艺术探索》 第一章 Activity的生命周期和启动模式
《Android开发艺术探索》 第九章 9.2节Activity的工作过程
Google-进程和应用生命周期
Google-任务和返回栈
来源:https://blog.csdn.net/u010577768/article/details/102749419