什么是 Android 组件化
Posted 陈旭金-小金子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是 Android 组件化相关的知识,希望对你有一定的参考价值。
前言
我希望阅读本篇博客的童鞋都是有一定的 android
基础的,并且近期想实施组件化方案的.希望这篇文章能帮助到你,让你知道什么是组件化,有哪些可选的方案
什么是组件化
下面这幅图相信大家平常多多少少都能看见类似的.
最下面一层是 基础组件层
, 包括但不止以下方面
- 存储
- 本地存储
- 数据库存储
- 文件存储
SP
存储- …
- 网络请求
- 实体对象
- 基础类
- BaseActivity、BaseFragment、BasePresenter、BaseViewModel…
- 公用的资源文件
- 公用的
Theme
- 公用的
Style
- 公用的
Color
- 公用的
Dimen
- 公用的
Drawable
- …
- 公用的
中间的是 业务模块
每一个 业务模块
彼此之间是没有任务的关系,彼此的代码和资源都是隔离的,并且不能够相互引用,每一个都是平行的关系.
总结
而组件化最关键的几点核心就是:
- 代码和资源的隔离
- 每一个业务模块都有
单独运行
的能力 - 壳工程可以任意的组合某几个
业务模块
打包出一个全新的Apk
以上的三点就是 Android
的组件化,用一句话来解释就是:
Android 组件化就是利用多个
Module
来表示应用的多个模块实现代码和资源的隔离,并且每个Module
都有单独运行和组合的能力.
实现上述的几点并不难,难点是如何在组件化之后,我们的代码还能像以前一样跳转和使用其他业务模块的功能
所以如何当你项目打算组件化了,你就要解决组件化之后会带来的两个核心问题
- 如何跳转
- 如何调用任意模块的功能
凡是解决了上面两个核心问题的方案都可称之为组件化方案.所以组件化方案到底解决了什么问题,每一个开发人都应该心里有一个数,而不是跟风使用组件化方案都不知道到底解决了什么问题,也不知道为什么要组件化.
如何去解决项目组件化带来的两个核心问题
如何跳转
因为每一个 业务模块
都是互相独立没有丝毫的关系的,所以以前的跳转方式和功能的调用都不管用了.比如以前的跳转
Intent intent = new Intent(this,XXX.class);
startActivity(intent);
因为上述代码明确指定了 XXX.class
为目标 Activity
,而组件化了之后你要跳转的 Activity
很可能是其他业务模块的,你是引用不到的,这时候你是没办法跳转过去的,那么如何去解决呢?
解决方案如下:
- 隐式跳转
- App Link
以上两种都是系统支持的方式,你完全可以采用这种方式去解决跳转的问题
如何调用任意模块的功能
每一个业务模块本质上是一个 Lib
,被壳工程包含这个模块的功能就在 Apk
当中了.
但是每一个 业务模块
的功能很可能需要被其他模块使用,而每一个 业务模块
又是代码和资源的隔离的.所以要实现 A 业务模块
的功能能被其他 业务模块
调用,那么 A 业务模块
就需要通过一定的方式供别的 业务模块
调用.
可以有的做法有:
- 服务发现
- 使用通用协议
- http 协议
- Android AIDL
- …
这一步的实现明显比跳转的难度更难.所以为什么会有各种各样的组件化的方案的诞生,他们就是帮助开发者解决这个问题的.
如果你自己去解决这些问题,你的代码会写的很恶心,如果你能解决这些问题并且代码还能写的很优雅,那么恭喜你,你也写了一个组件化方案
服务发现的实现思路
这里稍微说一下如何实现服务发现这种思路的功能
假设你的业务模块被加载和卸载会有一个类似于 生命周期
的概念,大白话就是壳功能加载和卸载 A 业务模块
, A 业务模块
都能知道
那么我们可以利用下面几步实现服务发现:
- 所有需要提供给外部的功能都封装成接口,放到一个
基础业务模块
- 在一个
基础业务模块
中放置一个Map
,key
为Class<T>
,value
为T
. - 当业务模块被加载的时候,把对应接口的实现类
put
到map
集合中 - 当业务模块被卸载的时候,把对应接口的实现类从
map
集合中remove
基础业务模块
是指每一个业务模块都会依赖的一个 业务模块
,虽然它叫业务模块,但是它内部不写具体的业务代码,而是为每一个 业务模块
提供一些基础公用的代码或者资源
上面的方式就可以解决这个问题,但是这里分享的只是解决这个问题的思路,我们还有一个前提条件要能被通知到业务模块被加载和卸载.
服务发现依赖的业务模块的生命周期的实现思路
当你业务模块被加载的时候,我上面一直在说这句话,其实模块的情况下, A 业务模块
被壳工程依赖,当运行的时候其实 A 业务模块
模块并不知道自己的状态.只是代码被包含到 Apk
了而已.
此时虽然壳工程依赖了 A,B,C 三个 业务模块
,但是当程序启动的时候,他们都不知道自己被依赖使用了,所以这里缺少一个所谓的通知,实现的方式不考虑维护性的话也有很多:
- 每一个
业务模块
都写一个广播,在壳工程加载的时候能被通知到 - 每一个
业务模块
都写一个Activity
,在壳工程加载某一个模块的时候,利用上面的跳转解决方案通知 - 每一个
业务模块
都在指定的包下创建一个特别的类,统一实现了某接口,在壳工程加载模块的时候,能反射到该类实现一个通知 - 类似的方法应该还有很多,我也就不一一列举了,总之就是实现了一个通知…
总结
正因为上面的两个原因阻挡了你组件化的进程,而上面两个核心问题你自己去解决不仅麻烦,而且不够通用,所以很多组件化方案应运而生.他们不管做的有多好,最根本的就是解决了上述的两个问题.每一个组件化方案都是如此.
有哪些代表性的组件化方案
以上的都是可用的一些方案,都从根本上解决了上述两个问题,并且每一个框架都有各自的优点和缺点.
上述的所有方案从性质上来分分为两类:
- 基于
URI
的 - 基于
Action
的
什么意思呢?
基于 URI
的就是框架的路由模块设计的核心是围绕 URI
设计的,天然的和 URL
等完美的结合.让一个 URI
和一个目标界面产生 1-1 的关系,给框架一个 URI
就可以路由到一个目标界面
基于 Action
的就是所有的操作都是用 Action
表示,至于你用 Action
去做什么,完全由你自己去实现,框架帮你做的就是在任何一个地方发起的一个 Action
,都能通知到对应模块,让指定模块去处理这个 Action
.
除了 CC
的其他方案都是基于 URI
设计的, CC
是基于 Action
设计的.两者并没有所谓的谁好谁坏,都已经解决了上述的两个核心问题.但是站在我的角度上看, Android 组件化
采用基于 URI
设计的方案会更好一些.毕竟 URI
是更加通用的一个协议,大家都对这个概念是有的,而且比较容易接受.虽然 CC
的 Action
也可以充当 URI
的 Path
,但是毕竟基于 URI
的还支持 Query
(即?后面的键值对) 的使用, 路由方面是肯定比不上基于 URI
的
回顾组件化带来的两个核心问题:
- 跳转
- 调用任意模块的功能
跳转方面,基于 URI
的明显会更好,这点是毋庸置疑的
调用任意模块的功能个人觉得通过服务发现的方式也会更好,因为接口调用都是显示调用的,方便后续的维护
选择方案的时候不仅仅要考虑框架提供的表面功能,一定要考虑维护性,我的建议很简单,一个框架应该尽可能的向以下几个方面靠拢
- 对项目的入侵尽可能的小
- 跳转和跨模块功能的调用尽可能的显示调用
- 跳转是不可能显示的,所以很多框架都会附带一个
IDEA Plugin
去帮助你把 跳转的地方和目标界面 联系在一起,达到一个"显示" 调用
- 跨模块功能的调用如何用的是服务发现,其实本身就是显示调用的
- 跳转是不可能显示的,所以很多框架都会附带一个
- 配置尽可能的简单明了
当然还有我没有想到的,有兴趣的可以留言.以上的框架中我推荐使用
Component,它是基于 URI
设计的,并且比其他的基于 URI
设计的功能更全,更完善,也更好用.欢迎大家使用
以上是关于什么是 Android 组件化的主要内容,如果未能解决你的问题,请参考以下文章