Android 启动 Activity 的多种方式
目次
我们知道,Activity 是 Android 界面层的核心承载形式,几乎所有用户可感知的内容,最终都会落在 Activity 上。所有的内容都是基于Activity的,无论是内容、弹窗,随着官方进一步收紧安全策略,除系统应用外,普通应用更难仅仅依靠无页面的Service形式长期存在。Activity之于Android的重要性自然不言而喻。
那么,Activity是怎么启动的呢?
原生方式 #
原生方式我们通常分成两种:显示启动和隐式启动。
显示启动 #
显示启动即需要知道要启动的Activity是哪个,是最最常用的当属官方提供的方式,写起来是这样:
| |
当需要携带数据或是设置flag时,无非是多写两行代码,就变成了这样:
| |
这种方式非常灵活,每次启动添加参数、flag非常方便,编译器就能知道启动的Activity是否存在。
除了出现过的这些,可以使用的还有设置luanchMode、添加动画甚至是权限,也可以使用ActivityResultLauncher接受新Activity的结果(startActivityForResult被弃用,不推荐使用)。
如果全都好,也就不会有这篇文章了对吧。它的不足是很繁琐,即使是按照相同的方式启动一个带有相同参数的Activity也要再写一遍,违反了DRY(Don’t Repeat Yourself)原则。
隐式启动 #
隐式启动和显示启动对应,也就是不知道要启动哪个具体Activity,只关心有什么特点,使用场景通常有几种:
- 基础功能Activity
- 外部Activity(三方登录)
- DeepLink(Scheme)
好处是调用方不需要关心启动的是Activity名称,携带必要的参数即可。略有的不足是编译期无法校验 Intent 是否能被正确解析,只有在运行时由系统进行匹配,问题往往要到执行阶段才能暴露。
从H5中执行切携带参数时可能存在参数编码问题,需要编码,可以使用Uri.parse或是Uri.appendQueryParameter方法
封装 #
工厂方法 #
封装中用到最多的当属工厂方法。
| |
这里是start,也可能是launch等等,将一个Activity启动的方法收敛到一个方法中,好处是很容易找到哪里启动,不足是通常不能满足所有情况,Kotlin中支持默认参数使用起来稍微些,Java中更严重,当需要不同参数情况很多时,往往要定义很多个相同名称的方法满足业务需要,看上去很乱。另外,一旦在 start 方法中掺杂业务判断,Activity 很容易从“页面”变成承载逻辑的入口,反而增加维护成本。
虽然封装了,使用时也一样是需要有所在模块的引入才能使用。
DSL / Builder #
这是我个人最喜欢的方式,利用Kotlin的扩展方法和reified关键字写出类似模板的代码。
写法像是这样:
| |
本质上只是封装了显示启动,写法更优雅,其他方面并无差异。一样支持编译器检查,一样需要依赖Activity所在模块。
完整定义可以参考我的Gist:opoojkk/ActivityKt.kt
三方库 #
当然可以说,本质上还是封装显示启动,不过是程度不同而已,当然是这样,要是这么说,就不存在其他的方式了,毕竟Android只支持显示启动和隐式启动🤷♀️。
拿使用最多的框架ARouter举例,启动上的确是封装了显示启动,写法上倒是有几分像隐式启动中的DeepLink:
| |
好处是写法上足够简单清晰,一目了然,带了哪些参数路径是什么,更重要的是不需要与要启动的Activity所在的模块有耦合,只关心启动,由框架完成注册。也因此支持在组件化工程中使用。 不足嘛,硬要挑还是不能在编译器检查,传入的路径仍然是字符串。如果支持了,恐怕也没办法支持组件化了。
对比 #
| 方式 | 编译期校验 | 参数集中管理 | 使用成本 | 组件化友好 | 灵活性 | 适用场景 |
|---|---|---|---|---|---|---|
| 原生显示启动 | 有 | 无 | 低 | 无 | 高 | 小型项目、简单页面跳转 |
| 原生隐式启动 | 无 | 无 | 中 | 低 | 高 | 系统能力、三方应用、DeepLink |
| 工厂方法 | 有 | 低 | 中 | 无 | 中 | 参数固定、跳转规则稳定的页面 |
| DSL / Builder | 有 | 中 | 中 | 无 | 高 | Kotlin 项目、追求代码整洁度 |
| ARouter 等三方库 | 无 | 高 | 高 | 高 | 高 | 组件化、多模块、大型工程 |
说明:
- “编译期校验”指是否能在编译阶段发现 Activity 不存在或参数错误
评论
评论由 giscus 提供;如果未加载,可能是网络环境阻止了 giscus。