Activity那些不为人知的秘密
Posted 姓chen的大键哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Activity那些不为人知的秘密相关的知识,希望对你有一定的参考价值。
Activity是android应用程序直接与用户交互的组件,在Activity中,除了它的用法,像生命周期和启动模式,这些内容没有深入学习很难理解其中的奥秘,接下来揭示Activity中那些不为人知的秘密。
文章目录
Activity的生命周期
Activity的生命周期分为两部分,一部分是正常情况下的生命周期,另一部分是异常情况下的生命周期
正常情况下的生命周期
正常情况下的生命周期是指在有用户参与的情况下Activity经过的生命周期的改变
在【Activity的生命周期】这里详细说明了正常情况下Activity的生命周期。
Activity生命周期切换过程如下图所示:
对于上图有几种情况:
- 某一个特定的Activity。第一次启动,调用方法:onCreate()→onStart()→onResume()
- 当用户打开新的Activity或切换到桌面时,调用方法:onPause()→onStop()。当新Activity使用透明主题,则当前Activity不调用onStop()
- 当用户再次回到原Activity时,调用方法:onRestart()→onStart()→onResume()
- 当用户按back键回退时,调用方法:onPause()→onStop()→onDestory()
- onCreate() 与onDestory() 是配对的,分别标识Activity的创建和销毁,有且只有可能调用一次;onStart() 与onStop() 是配对的,标识Activity的可见性,这两个方法可能会被调用多次;onResume() 与onPause() 是配对的,标识Activity是否在前台,这两个方法可能会被调用多次
若当前Activity为A,用户此时打开一个新的Activity B,那A会先执行onPause()方法,然后B执行onCreate()→onStart()→onResume(),最后A再执行onStop() 方法
异常情况下的生命周期
Activity被系统回收或当前设备Configuration发生改变从而导致Activity被销毁重建。
异常情况有两种:
资源相关的系统配置发生改变导致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按照优先级从高到低分为三类:
- 前台Activity:与用户交互的Activity,优先级最高
- 可见非前台Activity:Activity弹出一个对话框,导致Activity可见但位于后台无法与用户交互
- 后台Activity:已经被暂停的Activity,优先级最低
当系统内存不足时,系统会按照上述优先级去杀死目标Activity所在进程并在后续通过onSaveInstanceState() 和onRestoreInstanceState() 来存储与恢复数据,如果一个进程没有四大组件在执行,那这个进程会很快被杀死。
Activity启动模式
launchMode
【Activity的启动模式】这里介绍了Activity的启动模式的含义以及类型。
这里补充一些内容:
- 当使用ApplicationContext去启动standard模式的Activity会报错,因为standard模式的Activity默认会进入启动它的Activity所属的任务栈总,但非Activity类型的Context(ApplicationContext)没有任务栈,就会出错。解决办法是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动就会为其创建一个新的任务栈
- 任务栈分为前台任务栈和后台任务栈,后台任务栈中的Activity位于暂停状态,用户可以通过切换将后台任务栈调回前台
- singleTask启动模式中,有一个参数TaskAffinity,这个参数标识了一个Activity所需要的任务栈名字,默认情况下,所有Activity所需任务栈的名字为应用包名。TaskAffinity属性主要和singleTask启动模式或allowTaskReparenting属性配对使用:
- 当TaskAffinity与singleTask启动模式配对使用时,它是具有该模式的Activity的目前任务栈的名字,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中
- 当TaskAffinity与allowTaskReparenting属性配对使用时,当应用A启动了应用B的某个Activity后,如果这个Activity的allowTaskReparenting属性为true时,那当应用B被启动之后,此Activity会直接从应用A的任务栈转移到应用B的任务栈中。
- singleTask模式的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模式
Activity的Flag
标记位的作用很多,常用的标记位有:
- FLAG_ACTIVITY_NEW_TASK:为Activity指定singleTask模式启动
- FLAG_ACTIVITY_SINGLE_TOP:为Activity指定singleTop模式启动
- FLAG_ACTIVITY_CLEAR_TOP:有该标记的Activity启动时,在同一任务栈中所有位于它之上的Activity都要出栈。这个标记位一般和singleTask启动模式一起出现,在这种情况下被启动的Activity实例已经存在,那系统会调用它的onNewIntent() 方法。如果被启动的Activity采用standard模式启动,那它连同它之上的Activity都要出栈,系统会创建新的Activity实例并放入栈中。
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:有该标记的ACtivity不会出现在历史Activity的列表中,等同于xml属性:
android:excludeFromRecents="true"
IntentFilter匹配规则
启动Activity分为两种,显式调用和隐式调用。隐式调用需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息,不匹配则无法启动。为了匹配过滤列表,必须同时匹配过滤列表中的action、category、data信息。一个Activity可有多个intent-filter,一个Intent只要能匹配任何一组intent-filter即可成功启动对应的Activity。
action匹配规则
action是一个字符串,它的匹配规则是Intent中的action必须能够和过滤规则中的action匹配,与action字符串值完全一样,而且action区分大小写。
action的匹配要求Intent中的action存在且必须和过滤规则中的其中一个action相同。
category的匹配规则
category的匹配规则与action不同,它要求Intent中如果含有category,那么所有的category都必须和过滤规则中的其中一个category相同,Intent中可以没有category。
data匹配规则
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>://<host>:<port>/[<path>|<pathPattern>|<pathPrefix>]
- Scheme:URI的模式(http、file、content等),若没有指定,则整个URI无效
- Host:URI主机名,若没有指定,则整个URI无效
- Port:URI中的端口号,URI指定了scheme和host,port才是有意义的
- path:完整路径信息
- pathPattern:表示完整路径,可包含通配符
- pathPrefix:表示路径前缀信息
采用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);
以上是关于Activity那些不为人知的秘密的主要内容,如果未能解决你的问题,请参考以下文章