Android 开发艺术探索学习笔记(一)

结合 官方文档 阅读《Android 开发艺术探索》时所做的学习笔记。本篇记录第一章。

Activity 生命周期

  • 新 Activity 启动之前,原栈顶 Activity 的 onPause 会先被调用,所以不要在 onPause 中做耗时操作。
  • onRestoreInstanceState 方法会在 onStart 之后被调用,推荐在这里而不是 onCreate 方法中恢复数据。大部分 View 都会帮我们做一些基本的状态恢复,但是必须为 View 提供 ID,可以查看对应的 View 中的 onRestoreInstanceState 方法了解具体恢复了哪些内容。
  • onSaveInstanceState 方法会在 onStop 之后被调用(targetSdkVersion >= 28),可以在这里保存一些轻量的数据。它可以作为生命周期方法的补充,但是有些时候并不会被调用,比如从 A 中启动 B 的时候,如果 A 未被回收那么系统就不会调用 onSaveInstanceState。

Activity 的启动模式

四种启动模式

  • standard:每次都会在当前任务栈中启动一个新的 Activity 实例。使用场景:默认配置,商品详情页。
  • singleTop:栈顶复用,再次启动时会调用 onNewIntent() 而不是 onCreate()。使用场景:登录页、通知推送页。
  • singleTask:栈内复用,一般与 taskAffinity 结合使用,与 singleTop 类似,如果在请求的目标栈中已有实例存在,则会调用 onNewIntent()。另外,singleTask 启动模式还有 CLEAR_TOP 的效果,比如 A、B 两个 activity 位于同一个任务栈,如果 A 是 singleTask,而且被另一个任务栈的 activity 再次启动,则 B 会出栈。使用场景:App 主页面、WebView 页面、只能出现单一页面的场景。
  • singleInstance:加强的 singleTask,只能单独位于一个任务栈中。使用场景:系统 Launcher、锁屏键、来电显示等系统应用。

关于 taskAffinity

  • 指定 Activity 在哪个栈中运行,默认使用 applicationId。
  • 具有相同 taskAffinity 的 activity 会被放入同一个栈中。
  • 一般需要配合 singleTask 启动模式来使用。

关于 allowTaskReparenting

  • 当启动该 activity 的时候,如果该 activity 存在相同的 taskAffinity 的栈,则会被移动到那个栈里。比如一个浏览器应用 App A 中的 activity A 指定了 allowTaskReparenting,同时与主 activity 位于同一个任务栈中。那么当你在别的应用 App B 中,点击了一个链接打开了 A,然后再返回桌面打开浏览器应用,会直接打开 activity A 而不是主 activity,而当我们返回 App B 的时候,会发现这个 activity A 已经不在了(被移动到了 App A 的任务栈中)。
  • 我们一般结合 singleTask 并指定单独的栈来使用 allowTaskReparenting。

关于 Intent Flags

singleTask 和 singleInstance 的区别

  • singleTask 启动模式下,当栈和 activity 都不存在的时候,系统会创建一个新的 activity 放入新的栈内,如果栈存在则直接将 activity 入栈,而且除了相同 taskAffinity 的 activity 之外,还允许其他 activity (i.e. standard 和 singleTop 的 activity) 入栈。

  • singleInstance 启动模式下的 activity 不允许其他 activity 入栈,也就是说栈内永远只有这一个 activity。

IntentFilter 的匹配规则

关于 IntentFilter 的组成

  • 一组 IntentFilter 可以由多个 action category data 组成,必须同时匹配 action category data 才能成功启动对应的组件。
  • 如果该组件只能处理特定某几种的 action category data 组合,那么你就必须创建多个对应的 IntentFilter 而不是把他们全都放一起。

关于 通过匹配测试

  • action:指定接收动作类型,用 android:name 指定,可以有 0 个或者多个。一个 Intent 想要匹配成功,则必须至少匹配上一个 action,intent-filter 中可以指定比 Intent 中更多的 action。另外,
    • 如果 intent-filter 没有指定 action,则所有 Intent 都无法匹配成功;
    • 如果 Intent 没有指定 action,那么这个 Intent 会通过所有指定了 action 的 intent-filter。
  • cateogry:指定接收分类。如果 Intent 中指定了多个 category,那么必须全都匹配上才能匹配成功。其他与 action 相同。
    • 使用隐式 Intent 的时候,系统默认添加上一个 CATEGORY_DEFAULT,所以如果你想要匹配成功就必须添加 android.intent.category.DEFAULT
  • data:指定 URI 和 MIME type
    • URI 包括以下属性:scheme, host, port, path,host 有时也称为 authority。
      • 完整格式:<scheme>://<host>:<port>/<path>
      • 例子:content://com.example.project:200/folder/subfolder/etc
      • 每个属性都是可选的,但是有先后依赖关系,比如如果没有指定 scheme,那么即使指定了 host 或者 port 或者 path 也是无效的。
      • URI 支持部分匹配。
    • 完整 MIME type 列表可参考 IANA MIME Media Types,不同机型支持的 MIME type 有所不同。
    • URI 和 MIME type 匹配有如下规则:
      • 完全匹配,比如如果 Intent 只指定了 MIME type,那么所有只指定了 MIME type 都会通过匹配。
      • 如果 intent-filter 指定了 MIME type,那么默认支持 content: or file: URI,即如果 Intent 指定了该 MIME type 同时又指定了 scheme 为 content 或者 file 则可以匹配成功。
    • 如果要为 Intent 设置完整的 data 和 type,必须调用 setDataAndType,因为 setData 和 setType 会相互消除。

关于 Intent 匹配

  • 我们可以使用 PackageManagerresove... 以及 query... 方法,来确定是否存在可以调用的组件,前者会返回匹配到的最佳组件,后者返回所有匹配到的组件。
  • 记得使用 MATCH_DEFAULT_ONLY flag 来过滤掉没有声明 android.intent.category.DEFAULT 的组件。

系列文章