Activity是Android应用程序直接与用户交互的组件,在Activity中,除了它的用法,像生命周期和启动模式,这些内容没有深入学习很难理解其中的奥秘,接下来揭示Activity中那些不为人知的秘密。
Activity的生命周期分为两部分,一部分是正常情况下的生命周期,另一部分是异常情况下的生命周期
正常情况下的生命周期是指在有用户参与的情况下Activity经过的生命周期的改变
在【Activity的生命周期】这里详细说明了正常情况下Activity的生命周期。
Activity生命周期切换过程如下图所示:
对于上图有几种情况:
若当前Activity为A,用户此时打开一个新的Activity B,那A会先执行onPause()方法,然后B执行onCreate()→onStart()→onResume(),最后A再执行onStop() 方法
Activity被系统回收或当前设备Configuration发生改变从而导致Activity被销毁重建。
异常情况有两种:
例如当前Activity处于竖屏状态,此时突然旋转屏幕,由于系统配置发生了改变,默认情况下,Activity会被销毁并重新创建。
默认情况下如果Activity不做特殊处理,当系统配置发生改变后,Activity的生命周期如下图所示:
Activity在异常情况下终止,系统会调用onSaveInstanceState() 来保存当前Activity的状态,这个方法在onStop() 之前执行。正常情况下系统不会调用onSaveInstanceState() 。当Activity被重新创建后,系统会调用onRestoreInstanceState(),并将Activity销毁时onSaveInstanceState() 方法所保存的Bundle对象作为参数传递给onRestoreInstanceState() 和onCreate() 方法。
onSaveInstanceState() 只会在Activity即将被销毁且有机会重新显示的情况下才会调用,Activity是正常销毁时,系统不会调用onSaveInstanceState()
onRestoreInstanceState() 一旦被调用,其参数Bundle saveInstanceState一定是有值的不需要额外判断非空;
onCreate() 正常启动时,其参数Bundle saveInstanceState为空,使用onCreate() 恢复数据需要判断非空。
官方建议采用onRestoreInstanceState() 恢复数据
当Activity在异常情况下需要重新创建时,系统会默认保存当前Activity的视图结构,并且在Activity重启之后恢复这些数据。保存和恢复View层次结构,系统的工作流程如下:Activity被意外终止,Activity会调用onSaveInstanceState() 去保存数据;然后Activity会委托Window去保存数据;Window再委托上面的顶级容器去保存数据。顶层容器是ViewGroup,很可能是DecorView;最后顶层容器通知其子元素保存数据。
系统配置中有很多内容,如果当某项内容发生改变后,若不希望Activity重新创建,则可以给Activity指定configChanges属性
android:configChanges="orientation"
指定多个值用“|”连接。
configChanges属性有如下几种:
在这种情况下数据存储与恢复与第一种情况完全一致。Activity按照优先级从高到低分为三类:
当系统内存不足时,系统会按照上述优先级去杀死目标Activity所在进程并在后续通过onSaveInstanceState() 和onRestoreInstanceState() 来存储与恢复数据,如果一个进程没有四大组件在执行,那这个进程会很快被杀死。
【Activity的启动模式】这里介绍了Activity的启动模式的含义以及类型。
这里补充一些内容:
给Activity指定启动模式有两种方式:
xml方式:
<activity
android:name="cn.chenjianlink.android.FirstActivity"
android:launchMode="singleTask"/>
代码方式:
Intent intent = new Intent();
intent.setClass(MainActivity.this, SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
第二种方式优先级高于第一种,两种方式在限定范围上有所不同:第一种方法无法为Activity设定FLAG_ACTIVITY_CLEAR_TOP标识,第二种方式无法为Activity指定singleInstance模式
标记位的作用很多,常用的标记位有:
android:excludeFromRecents="true"
启动Activity分为两种,显式调用和隐式调用。隐式调用需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息,不匹配则无法启动。为了匹配过滤列表,必须同时匹配过滤列表中的action、category、data信息。一个Activity可有多个intent-filter,一个Intent只要能匹配任何一组intent-filter即可成功启动对应的Activity。
action是一个字符串,它的匹配规则是Intent中的action必须能够和过滤规则中的action匹配,与action字符串值完全一样,而且action区分大小写。
action的匹配要求Intent中的action存在且必须和过滤规则中的其中一个action相同。
category的匹配规则与action不同,它要求Intent中如果含有category,那么所有的category都必须和过滤规则中的其中一个category相同,Intent中可以没有category。
data的匹配规则和action类似,如果过滤规则中定义了data,那Intent中也要定义可匹配的data语法如下:
<data
android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mimeType="string" />
data由两部分组成,mimeType和URI。mimeType指媒体类型;URI包含数据较多,其结构如下:
:// : /[ | | ]
- Scheme:URI的模式(http、file、content等),若没有指定,则整个URI无效
采用PackageManager的resolveActivity() 方法或Intent的resolveActivity() 方法可以判断是否存在能匹配Intent的Activity,若不存在则返回值为空.PackageManager还提供了queryIntentActivities() ,这个方法会返回所有成功匹配的Activity信息
public abstract List<ResolveInfo> queryIntentActivities(@NonNull Intent intent, @ResolveInfoFlags int flags);
public abstract ResolveInfo resolveActivity(@NonNull Intent intent, @ResolveInfoFlags int flags);