Android源码阅读笔记(二)—— 启动模式
Android源码阅读笔记(二)—— 启动模式初章
1、为什么学习启动模式
Activity的启动模式其实是一个在面试中经常会被关注的问题,那么它的重要性体现在哪里?
A:在多数的开发场景中,我们似乎也没有怎么关注过它,不也都这么过来了?
高手:这便是“能用”与“好用”的区别了~~
一个项目成立之初,我们往往关注的是实现功能,快速地搭建基础框架,在应用层添砖加瓦,营造一个应用功能齐全的“假象”,而随着用户体量逐渐上升,一些问题往往会显露又抑或是场景可能逐渐变得复杂,不得不考虑更多的东西——优化,这其中就包括启动模式
启动模式能带来什么,又应当如何使用,别着急,一口气吃不成一个胖子,我们先看看好处:
- 内存优化的重要手段
- 用户体验保障的关键
- 应用架构设计的基础
- 安全性考虑的体现
这些优势都是建立在合理使用启动模式的前提之上的,是不是多了几分微薄的动力?
2、基础知识
任务和返回栈(https://developer.android.com/guide/components/activities/tasks-and-back-stack?hl=zh-cn)
启动模式(https://developer.android.com/guide/topics/manifest/activity-element?hl=zh-cn#lmode)
2.1、任务和返回栈
任务
- 用户在执行某个操作时与之交互的一系列
Activity
的集合 - 这些
Activity
按照启动的顺序排列在返回栈中
返回栈
- 暂且将它看成普通的栈数据结构(只不过存储的是
Activity
) - 调用
Activity
启动,该Activity
会被压入栈顶 - 用户点击返回按钮,会调用
finish()
,将栈顶Activity
从返回栈弹出
2.2、五种启动模式
- standard
- 大多数Activity的默认选择
- 每次启动都会创建新的Activity实例
- 可以是实例化多次,每个实例可以属于不同的任务,以及一个任务可以有多个实例
- singleTop
- 如果要启动的Activity已经在栈顶,则重用该实例,调用
onNewIntent()
- 如果不在栈顶,则创建新实例
- 适用场景:接收通知启动的页面
- 如果要启动的Activity已经在栈顶,则重用该实例,调用
- singleTask
- 如果要启动的Activity在栈中存在,则重用该实例,调用
onNewIntent()
- 会清除该Activity之上的所有Activity
- 适用场景:应用程序的主页面
- 如果要启动的Activity在栈中存在,则重用该实例,调用
- singleInstance
- Activity只能单独位于一个任务栈中
- 适用场景:需要与程序分离的页面
- singleInstancePerTask
- 在同一个任务栈中只能有一个实例
- 不同任务栈可以有自己的实例
- 不会创建新的任务栈(这是与singleInstance的主要区别)
3、初步了解
通过启动模式,可以定义如何将新的Activity
实例与当前的任务关联
首先,我们先看一下定义启动模式的两种方式:
- 使用清单文件
- 使用
intent
标志
3.1、使用清单文件定义启动模式
在清单文件中声明Activity
的时候,可以指定使用<activity>
中的launchMode
指定启动模式
3.2、使用intent
标志定义启动模式
启动Activity
的时候,也可以通过向startActivity()
中传递intent
的标志位,修改默认的行为
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_SINGLE_TOP
FLAG_ACTIVITY_CLEAR_TOP
3.3、具体使用
通过下面这个命令,可以查看应用返回栈相关的信息
adb shell dumpsys activity | grep "<应用包名>"
比如说,现在我启动一个A活动,此时返回栈应该会有一个A
可以看到启动模式是标准,这是默认的类型,除此以外,可以了解到当前任务id是#19
标准模式
此时增加B、C两个活动,然后A->B->C串连启动,此时页面上是C
对应的返回栈入栈应该是上图所示,随后我们使用命令验证一下:
可以看到确实栈顶到栈底是CBA的顺序,并且总共 3 个活动
接下来,再用A每次启动A,A->A->A,标准模式下每次启动都会创建实例
singleTop(栈顶复用)
这里需要分在栈顶和不在栈顶的情况进行讨论,这里我们可以将B标记为singleTop
,如果其在栈顶将会直接使用,如果不在就重新创建
B在栈顶:
比如先启动A->B,然后再启动B
B在栈顶时不再创建实例,但会调用栈顶B的onNewIntent()
方法
B不在栈顶:
此时,就和标准模式一样,创建新的实例
singleTask(栈内复用)
如果当前的返回栈是有的,那么启动该活动时会清空上面所有的活动,并触发对应活动的onNewIntent()
此时,设置活动 A 的启动模式为singleTask
,A->B->C
然后,再从C启动A,此时候入栈的B、C会被清除出栈
参考以下过程:
日常的场景可能就比如支付页面,或者应用首页这种体量比较大的页面,像支付宝付款结束后,中间页面订单防止重复触发,并且反馈结果给原来页面onNewIntent()
处理;而复杂的主页功能入口比较多,又要保证唯一
singleInstance(全局单例)
全局单例的启动模式会开启一个新的独立任务栈,并且保证全局单例,也可以覆盖在其他应用之上,用于比较特殊的场景
如果给A使用singleInstance
,就会使得A独立使用一个任务栈
singleInstancePerTask(全栈单例)—— Android 10(API 29)新增
该模式每个任务栈只会有一个实例,不会创建新的任务栈,不同的任务栈可以有实例
这个模式结合了singleInstance
的独占性和多窗口的灵活性,适合现代Android应用的多窗口场景
该模式后期再结合多窗口说吧
https://www.jianshu.com/p/2a9fcf3c11e4
https://juejin.cn/post/6844904069413208078
https://www.jianshu.com/p/94816e52cd77