Android 开发艺术探索笔记之一 -- Android 的生命周期和启动模式

学习内容:

  • Activity 的生命周期和启动模式以及 IntentFilter 的匹配规则分析
    • 异常情况下的生命周期
    • Activity 的启动模式以及 Flags
    • 隐式启动下的 Intent 匹配

Activity 的生命周期全面分析

我的另一篇文章:详解 Android&Fragment 的生命周期
此处只是记录一下缺失的知识点,加以扩充。
建议本文与上文配合阅读。

前置

生命周期分为两类:

  1. 典型情况下的生命周期
  2. 另一部分是异常情况下的生命周期。

典型情况:指有用户参与的情况下,Activity 所经过的生命周期的改变
异常情况:指 Activity 被系统回收或者由于当前设备的 configuration 发生改变从而导致 Activity 被销毁重建

典型情况下的生命周期

书中的两个问题:

  1. 问题一:onStart 和 onResume、onPause 和 onStop 有什么实质性的不同?
    答:意义不同:onStart 和 onStop 是从Activity 是否可见这个角度来回调;onResume 和 onPause 是从 Activity 是否处在前台这个角度来回调

  2. 问题二:当前 Activity 为 A,此时打开新的 Activity B,那么 B 的 onResume 和 A 的 onPause 哪个先执行?
    答:旧的Activity(A)执行完 onPause 之后,新的 Activity(B)的 onResume 才能执行。
    这个问题在我原来写的那篇文章中也提到过,只不过我得出这个结论是写了个 Demo,测试得来的。而主席这里,从源码分析得到结论,之后再通过实例测试验证结论。可以说,我只是知道了这个结论就是这样,但是却不知道为什么,原理是什么。

异常情况下的生命周期

书中的两种情况:

  1. 情况1:资源相关的系统配置发生改变导致 Activity 被杀死并重新创建。
    1. 系统配置发生改变后,Activity 会被销毁,onPause、onStop、onDestroy 正常调用。
    2. Activity 是异常情况下终止,因而会调用 onSaveInstanceState 方法保存状态。
    3. 异常情况下终止,需要重建时,系统会默认为我们保存当前 Activity 的视图结构。
    4. 保存和恢复 View 层级结构的工作流程(以保存为例,恢复过程类似):委托思想
      1. 首先 Activity 意外终止时,调用 onSavedInstanceState 保存数据
      2. 然后 Activity 委托 Window 保存数据。
      3. 接着 WIndow 再委托它上面的顶级容器保存数据(顶级容易是一个 ViewGroup 一般是 DecorView)
      4. 最后顶级容器一一通知它的子元素来保存数据。
  2. 情况2:资源内存不足导致低优先级的 Activity 被杀死
    1. 优先级排序
      1. 前台 > 可见但非前台 -> 后台
    2. 后台任务保活:将后台任务放入 Service 从而保证进程有一定的优先级,这样不会轻易被杀死。

如何在系统配置发生改变后,避免 Activity 重建?

答:通过为 Activity 指定 configChanges 属性,以此在特定情况下不让系统重建 Activity,此时系统不会保存恢复数据,而是调用了 onConfigurationChanged 方法,在此方法中我们可以做一些自己的特殊处理。
常用的属性为 locale、orientation、keyboardHidden 这三个:locale 指设备本地设置发生改变,一般是系统语言;orientation 指屏幕方向发生改变;keyboardHidden 指键盘的可访问性发生了改变,比如用于调出了键盘。


Activity 的启动模式

我的另一篇文章:深入理解 Android 的启动模式
此处只是记录一下缺失的知识点,加以扩充。
建议本文与上文配合阅读。

Activity 的 LaunchMode

四种模式:

  1. standard:标准模式
  2. singleTop:栈顶复用模式
  3. singleTask:栈内复用模式
  4. singleInstance:单实例模式

singleTask 下的特殊情况:

假设目前有 2 个任务栈,前台任务栈为 AB,后台任务栈为 CD,且 CD 的启动模式都是 singleTask:

  1. 如果此时启动 D:整个后台任务栈都被切换到前台,即前台此时为 ABCD。
  2. 如果此时启动 C:那么 D 会因为 singleTask 模式默认具备 clearTop 的效果而出栈,此时前台任务栈为 ABC。

TaskAffinity 基础

  • TaskAffinity 标识 Activit有所需要的任务栈的名字。
  • 该属性主要与 singleTask 启动模式或者 allowTaskReparenting 属性配对使用。

TaskAffinity + singleTask

待启动的 Activity 会运行在名字和 TaskAffinity 相同的任务栈中

TaskAffinity + allowTaskReparenting

当一个应用 A 启动了应用 B 的某个 Activity 后,如果这个 Activity 的 allowTaskReparenting 属性为 true 的话,那么当应用 B 被启动后,此 Activity 会直接从应用 A 的任务栈转移到应用 B 的任务栈中。

Activity 的 Flags

见我的另一篇文章:深入理解 Android 的启动模式


IntentFilter 的匹配规则

启动 Activity 分为两种:显式调用和隐式调用。

  1. 显式调用需要明确指定被启动对象的组件信息,包括包名和类名
  2. 隐式调用不需要明确指定组件信息。
  3. 如果二者共存,以显式调用为主。

隐式调用的匹配规则

隐式调用需要 Intent 能够匹配目标组件的 IntentFilter 中所设置的过滤信息,如果不匹配将无法启动目标 Activity。

IntentFilter 中的过滤信息有 action、category、data。

  1. 当三者都匹配时,IntentFilter 才匹配成功,才能启动目标 Activity。
  2. 一个 Activity 可以有多个 intent-filter,一个 Intent 只要匹配任何一组 intent-filter 即可成功启动对应的 Activity

1.action 的匹配规则

action 的匹配规则是 Intent 中的 action 必须能够和过滤规则中的 action 匹配,这里匹配指 action 的字符串值完全一致。

一个过滤规则中可能有多个 action,只要 Intent 中的 action 能够和过滤规则中的任何一个 action 相同都可匹配成功。反之,任何一个都不相同,则匹配失败。

需要注意:action 区分大小写。

2.category 的匹配规则

Intent 可以没有 category,但是如果一旦有 category,那么不管有几个,每个都要能和过滤规则中的任何一个 category 相同。

startActivity 或者 startActivityForResult 方法启动 Activity 时,默认会为 Intent 加上 android.intent.category.DEFAULT 这个 category。

3.data 的匹配规则

和 action 的匹配规则类似,Intent 必须含有 data 数据,并且 data 数据能够完全匹配过滤规则中的某一个 data,这里的完全匹配是指过滤规则中出现的 data 部分也出现在了 Intent 中的 data。

先介绍下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。mime Type 指媒体类型,比如 image/jpeg、audio/mpeg4-generic 和 video/* 等,可以表示图片、文本、视频等不同的媒体格式;而 URI 包含的数据很多,URI 的结构如下:

<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]

//实例
content://com.example.project:200/folder/subfolder/etc
http://www.baidu.com:80/search/info
  • Scheme:URI 的模式,如 http、file、content等,如果未指定,则 该URI无效。
  • Host:URI 的主机名,比如 www.baidu.com,如果未指定,则 该URI无效
  • Port:URI 的端口号,比如 80。
  • Path、pathPattern 和 pathPrefix:三者表述路径信息,其中 path 表示完整路径信息;pathPattern 也表示完整的路径信息,但是可以包含通配符 “*”;pathPrefix 表示路径的前缀信息。

下面说一下 data 的指定:

  1. 指定完整的 data,必须调用 setDataAndType 方法
  2. setData 和 setType 互相冲突,会彼此消除对方的值。

避免隐式调用出错

当隐式启动一个 Activity 的时候,如果未加判断,找不到匹配的 Activity,那么会抛出 android.content.ActivityNotFoundException 异常,解决方案:

  1. PackageManager 的 resolveActivity 方法或者 Intent 的 resolveActivity 方法:返回最佳匹配的 Activity 信息,如果找不到匹配的 Activity 就会返回 null
  2. PackageManager 的 queryIntentActivities 方法:返回 所有成功匹配的 Activity 信息。

补充说明:

  1. 不含 DEFAULT 这个 category 的 Activity 无法接收隐式 Intent
  2. 对于 Service,尽量使用显式调用方式来启动服务
  3. 通过以下设置表明入口 Activity,并且出现在系统的应用列表:

    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />

猜你喜欢

转载自blog.csdn.net/whdAlive/article/details/81229704
今日推荐