《Android 开发艺术探索》笔记 - Activity - 01

Activity 生命周期

正常情况下 Activity 的生命周期

  1. onCreate: 表示 Activity 正在被创建, 主要在这个方法中做一些初始化.

  2. onRestart: 表示 Activity 正在重新启动, 一般情况下, 当 Activity 从不可见变为可见时会被调用.

  3. onStart: 表示 Activity 正在启动, 但还无法与用户交互, 此时 Activity 还在后台.

  4. onResume: 表示 Activity 已经可见了, 和 onStart 的区别是现在 Activity 处于前台.

  5. onPause: 当其他 Activity 进入前台时会被调用, 这个方法里不能执行耗时的操作, 否则会影响新 Activity 的启动, 因为新 Activity 的 onResume 会在这个 Activity 的 onPause 执行完成后才会被调用.

  6. onStop: 表示 Activity 即将停止, 可以做一些回收工作, 但也不能太耗时.

  7. onDestroy: 表示 Activity 即将被销毁, 这是最后一个回调.

Activity Life Cycle

上图来源于: https://developer.android.google.cn/reference/android/app/Activity.html

Activity 的生命周期回调方法分为一下几种情况:

  1. 首次启动, onCreate -> onStart -> onResume.

  2. 当用户切换回桌面或其他 Activity 时, onPause -> onStop. 特殊情况: 如果新的 Activity 使用了透明主题, 那么当前 Activity 的 onStop 不会被调用.

  3. 用户再次回到这个 Activity 时, onRestart -> onStart -> onResume.

  4. 当用户按下 返回键 时, 默认情况下 Activity 会被销毁, onPause -> onStop -> onDestroy.

  5. onCreateonStop 对应着 Activity 的 创建销毁onStartonStop 对应着可见不可见onResumeonPause 对应着 前台后台.

异常情况下 Activity 的生命周期

  1. 系统资源发生改变

    比如屏幕方向发生变化时. 在默认情况下, Activity 会被销毁, 正常的生命周期方法会被调用, 并且 onSaveInstanceStateonRestoreInstanceState 会被额外调用, 用于保存和恢复当前状态.
    onSaveInstanceState会在onStop之前被调用, onRestoreInstanceState 会在 onStart 之后被调用.
    并且, Activity 会自动帮我们保存一些数据, 比如用户在文本框中输入的文本等等.
    View 也有 onSaveInstanceStateonRestoreInstanceState 两个方法, 当 Activity 意外停止时, 他们会被调用.

  2. 系统资源不足导致 Activity 被销毁

    Activity 的优先级如下:

    1. 前台 Activity, 即正在和用户交互的 Activity.
    2. 可见, 但是在后台的 Activity.
    3. 在后台的 Activity, 并且不可见.

    前台 Activity 最不容易被杀死.

    在这种情况下, onSaveInstanceStateonRestoreInstanceState 也会被调用.

Activity 的启动模式 (Launch Mode)

  1. standard: 标准模式, 也是默认的模式. 可以有多个实例, 在哪个 Activity 栈中启动, 就会被至于那个栈中.

  2. singleTop: 这种模式下, 如果这个 Activtiy 处于栈顶的话, 再次启动这个 Activity 并不会启动一个新的实例, 而这个 Activity 的 onNewIntent 会被调用, 可以在这个方法里处理新的请求.

    “standard”和“singleTop”Activity 为一类, 使用“standard”或“singleTop”启动模式的 Activity 可多次实例化. 实例可归属任何任务, 并且可以位于 Activity 堆栈中的任何位置.

  3. singleInstance: 该启动模式的 Activity 启动后会创建一个新的任务栈, 并且这个任务栈中始终只会有这一个实例, 如果再次启动, 不会创建新实例, 而 onNewIntent 会被调用.

  4. singleTask: 该模式也是一种单实例模式, 如果当前任务栈中不存在这个 Activity , 则会创建这个 Activity, 否则会将已存在的实例调到栈顶, 并且会清除掉原来所有的在该 Activity 之上的 Activity, 如下图:

    SingleTask Mode

    “singleTask”和“singleInstance”模式同样只在一个方面有差异: “singleTask”Activity 允许其他 Activity 成为其任务的组成部分. 它始终位于其任务的根位置, 但其他 Activity(必然是“standard”和“singleTop”Activity)可以启动到该任务中. 相反, “singleInstance”Activity 则不允许其他 Activity 成为其任务的组成部分.

另外, 在 singleTop, singleTask, singleInstance 在栈顶时, 再次启动该 Activity 时, 生命周期调用顺序为: onPause -> onNewIntent -> onResume.

Activity Flags

  1. FLAG_ACTIVITY_NEW_TASK

    这个 flag 效果与在 xml 中指定 singleTask 效果相同.

  2. FLAG_ACTIVITY_SINGLE_TOP

    这个 flag 效果与在 xml 中指定 singleTop 效果相同.

  3. FLAG_ACTIVITY_CLEAR_TOP

    具有此标记为的 Activity 在启动时, 在同一个任务栈中的所有位于它上面的 Activity 都要出栈.
    一般这个 flag 与 FLAG_ACTIVITY_NEW_TASK 配合使用.
    如果被启动的 Activity 是 standard 启动模式, 那么连同它自己以及它之上的 Activity 都要出栈.
    系统会重新创建该 Activity 的实例, 并放入栈顶.

  4. FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

    这个 flag 可以使启动的 Activity 不在最近任务列表中显示.

IntentFilter 匹配规则

Activity 的启动分为 显式启动 和 隐式启动.

显式启动通过直接指定包名组件名直接启动对应的组件.

隐式启动不需要指定特定的包名或组件名, 可以通过某些信息匹配某些 Activity.

intent-filter 中有三种过滤信息, action, category, data, 而在 startActivity 时,
必须匹配这三个信息, 否则会匹配失败.
一个组件中可以有多个 intent-filter, 只要匹配到其中任意一个即可.

  • action

    action 的匹配要求 Intent 中的 action 字符串与过滤器中声明的相同, 并且区分大小写.
    在单个 intent-filter 中可以定义多个 action, 而 Intent 中仅需要匹配到任意一个即可.

  • category

    Intent 可以添加多个 category, Intent 中如果含有 category,
    那么 Intent 中添加的 category 必须都在 intent-filter 中声明, 否则会匹配失败.

    另外, 在 intent-filter 中必须添加一个 android.intent.category.DEFAULT, 否则它不会被任何 Intent 匹配到,
    因为

    Android 会自动将 CATEGORY_DEFAULT 类别应用于传递给 startActivity() 和 startActivityForResult() 的所有隐式 Intent. 因此, 如需 Activity 接收隐式 Intent, 则必须将 “android.intent.category.DEFAULT” 的类别包括在其 Intent 过滤器中(如上文的 示例所示).

  • data

    使用一个或多个指定数据 URI 各个方面(scheme、host、port、path 等)和 MIME 类型的属性, 声明接受的数据类型.

    语法如下:

    1
    2
    3
    4
    5
    6
    7
    <data android:scheme="string"
    android:host="string"
    android:port="string"
    android:path="string"
    android:pathPattern="string"
    android:pathPrefix="string"
    android:mimeType="string" />
    • scheme

      URI 的模式, 比如 http, ftp, file, content 等, 这个是必选项, 如果不指定这个参数, 那么这个 URI 就是无效的.

    • host

      URI 的主机名, 比如 www.google.com, host 也是必须的.

    • port

      主机的端口号, 比如 80.

    • path | pathPattern | pathPrefix

      表示 URI 的路径, path 表示完整的路径, pathPattern 中可以包含通配符, pathPrefix 表示路径的前缀

    • mimeType

      表示文件的类型, 比如 image/png, text/html 等等.

在被启动的 Activity 中可以通过 Intent#getData 获取到匹配到的 URI.

在 Intent 中可以通过 Intent#setData, Intent#setType 设置 data 或 type.
如果需要同时设置 data 和 type 则需要调用 Intent#setDataAndType, 因为上面两个方法都会清空另一个的值.

如果 intents-filters 仅设置了 mimeType, 而没有设置 scheme, scheme 默认会是 filecontent.

在隐式启动过程中, 如果没有匹配到任何组件, 那么程序会报错停止运行.
通常需要在隐式启动时, 判断是否有 Activity 可以匹配到.

有两种方法:

  1. 通过 PackageManager 的一系列 resolve...() 来查询, 如果可以匹配, 则会返回响应 Intent 的最佳组件, 否则就会返回 null.
  2. PackageManager 该提供了一系列的 query...() 的方法, 它会返回所有可以匹配的 Activity.

两种方法都不会直接激活组件, 而只是列出可以响应的组件.

1
2
public abstract List queryIntentActivities(Intent intent, int flags);
public abstract ResolveInfo resolveActivity(Intent intent, int flags);

注意第二个参数, 最好使用 MATCH_DEFAULT_ONLY

1
2
3
4
5
6
7
/**
* Resolution and querying flag: if set, only filters that support the
* {@link android.content.Intent#CATEGORY_DEFAULT} will be considered for
* matching. This is a synonym for including the CATEGORY_DEFAULT in your
* supplied Intent.
*/
public static final int MATCH_DEFAULT_ONLY = 0x00010000;

这个标记的含义是仅仅匹配那些在 intents-filters 中声明了 android.intent.category.DEFAULT 的 Activity,
如果 Activity 没有声明这个 category, 使用 startActivity 将会报错.

参考资料:

https://developer.android.google.cn/guide/topics/manifest/activity-element.html

https://inthecheesefactory.com/blog/understand-android-activity-launchmode/en

http://blog.csdn.net/javazejian/article/details/52072131

https://developer.android.com/guide/components/intents-filters.html