# 读 Android 开发艺术探索 &1

关键词:Activity / 生命周期 / 启动模式 / IntentFilter

Activity 是界面,除了 Window / Dialog / Toast 我们见到的就只有 Activity 了。 本次梳理主要包括:Activity 生命周期、启动模式、IntentFilter 匹配规则。

1. Activity 生命周期 #

一. 典型情况下的生命周期:被用户参与的生命周期的改变
二. 异常情况下的生命周期:被系统回收或由于当前设备的 Configuration 发生改变,导致 Activity 被销毁重建


【一. 典型情况下的生命周期】

onCreate:做一些初始化的工作(setContentView 加载界面布局资源、初始化 Activity 所需的数据)
onStart:正在启动,Activity 已经可见但是没有出现在前台,无法与用户交互,还处于后台
onResume:Activity 已经可见而且开始活动,可以与用户交互,已处于前台
onPause:正在停止,紧接着调用 onStop
onStop:就要即将停止,做不太耗时的重量级的回收工作
onRestart:由不可见到重新可见,用户行为导致 onPause → onStop → 回到该 Activity
onDestroy:即将被销毁,生命周期的最后一个回调,回收和最终资源的释放


Activity 生命周期切换的过程

对此需要知道的几种情况 ↓
1. 用户按下 back 键返回时,回调 onPause → onStop → onDestroy
2. 用户打开新的 Activity 或切换到桌面,回调 onPause → onStop ,如果新 Activity 采用的是透明主题,就不会回调 onStop 了
3. 第一次启动,回调 onCreate → onStart → onResume ; 再次回到 Activity,回调 onRestart → onStart → onResume
4. 就整个生命周期而言,onCreate 和 onDestroy 只会被回调一次
5. onStart 和 onStop 是从 Activity 是否可见的角度来设计回调的,onResume 和 onPause 是从 Activity 是否位于前台的角度来回调的
6. 当前为 Activity A ,这时用户打开了 Activity B,过程是:A 先 onPause,然后 B onResume(对于 Android 运行的机制在不同的 Android 版本上具有延续性,各个版本应该都是这个顺序)
7. 不同在 onPause 中做重量级的操作,因为必须执行完 onPause ,新的 Activity 才能 onResume,应该尽量在 onStop 中做操作,从而使新的 Activity 能够尽快的显示出来并切换到前台

【二. 异常情况下的生命周期】

有两种情况:
资源相关的系统配置发生改变导致 Activity 被杀死并重新创建
资源不足导致优先级较低的 Activity 被杀死

关于第 1 种情况需要知道的几点 ↓

  1. 当应用程序启动时,系统会根据当前的设备情况去加载合适的 Resources 资源
  2. 如果系统配置发生了改变,比如,竖屏切换至横屏,而我们的 Activity 没有做特殊的处理,就会被销毁并重新创建,参考图2
  3. 看图2就知道,在异常情况下终止的 Activity,系统会调用 onSaveInstanceState 来保存当前的 Activity 状态,onSaveInstanceState 方法的调用时机是在 onStop 之前(该方法只会出现在 Activity 被异常终止的情况下,正常情况下不会回调该方法)
  4. 而当 Activity 被重新创建的时候,系统会调用 onRestoreInstanceState 方法,Activity 销毁时 onSaveInstanceState 方法所保存的 Bundle 对象作为参数传给 onRestoreInstanceState 和 onCreate 方法
  5. 有个区别: onRestoreInstanceState 一旦被调用,其参数 Bundle saveInstanceState 是一定有值的,而 onCreate 是正常启动的话,其参数 Bundle saveInstanceState 为 null,要额外判断是否为空
  6. 通过 onRestoreInstanceState 和 onCreate 方法可以判断 Activity 是否被重建了,如果是的,就取出之前保存的数据并且进行恢复
  7. onRestoreInstanceState 的调用时机发生在 onStart 之后(从时序上说)
  8. 在 onSaveInstanceState 和 onRestoreInstanceState 方法中系统自动为我们做了一些恢复工作,比如文本框用户输入的数据、ListView 滚动的位置这些 View 相关的状态

异常情况下的 Activity 的重建过程

PS:关于保存和恢复 View 层次结构
系统的工作流程是一种典型的委托思想,上层委托下层,父容器委托子元素去处理一件事情(View 的绘制,事件的分发也都是采用类似的思想)。
过程大概是:

  1. Activtiy 被意外终止,会调用 onSaveInstanceState 去保存数据
  2. Activity 会委托 Window 去保存数据
  3. Window 再委托它上面的顶级容器去保存数据(顶级容器是一个 ViewGroup,一般说很可能是 DecorView)
  4. 顶级容器再去一一通知它的子元素来保存数据,至此整个数据保存过程就完成了

关于第 2 种情况需要知道的几点 ↓

  1. 数据存储与恢复过程与情况一完全一致
  2. Activity 的优先级:前台 Activity > 可见但非前台 Actiivty > 后台 Activity
  3. 前台 Activity 正在和用户交互,优先级最高
  4. 可见但非前台 Activity 比如弹有对话框的 Activity
  5. 后台 Activity 被暂停的 Activity 比如执行了 onStop 优先级最低
  6. 一般将后台工作放在 Service 中以保证具有一定的优先级,就不会轻易被系统杀死,如果一个进程中没有四大组件在执行,很快便会被系统杀死

PS:让 Activity 在屏幕旋转的时候不重新创建:

 android:configuration="orientation|screenSize"

2. Activity 启动模式 #

【目前有四种启动模式】

模式中文名
standard:标准模式
singleTop:栈顶复用模式
singleTask:站内复用模式
singleInstance:单实例模式

在默认情况下,多次启动同一个 Activity 的时候,系统会创建多个实例并把它们一一放入任务栈中,单击 back 键的时候,这些 Activity 会回退。任务栈“后进先出”的栈结构,栈中没有 Activity 的时候系统会回收这个任务栈。

  1. standard 模式:
    系统默认模式。谁启动了这个 Activity, 这个 Activity 就进入谁的任务栈中。比如,A 启动了 B(B是标准模式),则 B 会进入 A 所在的任务栈中。
  2. singleTop 模式:
    新 Activity 已经位于栈顶,则不会被重新创建,onNewIntent 方法被回调;若实例已存在但不位于栈顶,那么新的 Activity 仍然会重新重建。
  3. singleTask 模式:
    单实例模式。对于 Activity A 是 singleTask 模式,系统会首先寻找是否存在 A 想要的任务栈,如果不存在,就创建一个任务栈,然后创建 A 的实例之后把 A 放在栈中。如果存在 A 需要的任务栈,就再看是否存在 A 的实例在栈中,如果有实例存在就把 A 调到栈顶并调用它的 onNewIntent 方法(站内复用原则),如果实例不存在就创建 A 的实例并把 A 压入栈中。
  4. singleInstance 模式:
    单实例模式。singleTask 的加强版。加强的一点:这种 Activity 只能单独的位于一个任务栈中(创建新的任务栈)。

举有个小板栗:目前站内的情况是 ABCD,A 是栈底,D 在栈顶。如果 D 的启动模式为 singleTop 那么站内的情况仍然是 ABCD;如果 D 的启动模式为 standard,那么由于 D 被重新创建。导致站内的情况变为 ABCDD;

PS:什么是所谓的 Activity 需要的任务栈?
- 任务栈有一个参数:TaskAffinity,任务相关性。TaskAffinity 标识了一个 Activity 所需要的任务栈的名字。默认为应用的包名。也可单独指定自定义 TaskAffinity 属性。TaskAffinity 主要和 singleTask 或者 allowTaskPeparenting 属性配对使用,否则没有意义;
- 任务栈分为 前台任务栈 和 后台任务栈(Activity 位于暂停状态,用户通过切换将后台任务栈再次调用到前台);

【给 Activity 指定启动模式的两种方式】

方式一:AndroidManifest.xml

<activity
    android:name="io.github.isayes.MainActivity"
    android:configuration="screenLayout"
    android:launchMode="singleTask"
    android:label="@string/app_name" />

方式二:在 Intent 中设置标志位

Intent intent = new Intent();
intent.setClass(MainActivity.this, SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 实际上就是 singleTask 模式
startActivity(intent);

对此需要知道的以下几点 ↓

  1. 第二种方式的优先级要高于第一种,如果两种同时使用,则方式二覆盖方式一
  2. 第一种方式直接为 Activity 设定 FLAG_ACTIVITY_CLEAR_TOP 标识
  3. 第二种方式无法为 Activity 指定 singleInstance 模式
  4. singleTask 模式的 Activity 切换到栈顶会导致它上面的栈内的 Activity 出栈

【 Activity 的 Flags】

大部分情况下,我们不需要为 Activity 指定标记位

  • FLAG_ACTIVITY_NEW_TASK = XML singleTask
  • FLAG_ACTIVITY_SINGLE_TOP = XML singleTop
  • FLAG_ACTIVITY_CLEAR_TOP 一般和 singleTask 模式一起出现,当它启动时,在同一个任务栈中所有位于它上面的 Activity 都要出栈
  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = XML android:excludeFromRecents="true"

3. IntentFilter 匹配原则 #

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

显示调用需要明确地指定被启动对象的组件信息,包括包名类名;
隐式调用不需要明确指定组件信息;

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

关于 action 所要知道的几点 ↓

  1. 我们可以自定义
  2. 匹配规则:Intent 中的 action 必须能够和过滤规则中的 action 匹配(action 字符串完全一样)
  3. 区分大小写

关于 category 所要知道的几点 ↓

  1. 是一个字符串
  2. 系统中预定义了,但是我们也可以自定义
  3. action 要求 Intent 中必须有一个 action 与过滤规则中的某个 action 相同,而 category 要求 Intent 中可以没有 category,而一旦有就需要有匹配

关于 data 所需要知道的几点 ↓

  1. 如果过滤规则中定义了 data ,则 Intent 中必须也要定义可匹配的 data。
  2. data 的语法:两部分组成,URI + mimeType

    <data android:scheme="string"  
        android:host="string"
        android:port="string"
        android:path="string"
        android:pathPattern="string"
        android:pathPrefix="string"
        android:mimeType="string" />
  3. mimeType 媒体类型比如 image/jpeg、 audio/mpeg4-generic 和 video 等
  4. URI <scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]

End.

Note by HF.
Learn from 《Android 开发艺术探索》


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值