什么是 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, keyClass<T>, valueT.
  • 当业务模块被加载的时候,把对应接口的实现类 putmap 集合中
  • 当业务模块被卸载的时候,把对应接口的实现类从 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 是更加通用的一个协议,大家都对这个概念是有的,而且比较容易接受.虽然 CCAction 也可以充当 URIPath,但是毕竟基于 URI 的还支持 Query(即?后面的键值对) 的使用, 路由方面是肯定比不上基于 URI

回顾组件化带来的两个核心问题:

  • 跳转
  • 调用任意模块的功能

跳转方面,基于 URI 的明显会更好,这点是毋庸置疑的
调用任意模块的功能个人觉得通过服务发现的方式也会更好,因为接口调用都是显示调用的,方便后续的维护

选择方案的时候不仅仅要考虑框架提供的表面功能,一定要考虑维护性,我的建议很简单,一个框架应该尽可能的向以下几个方面靠拢

  • 对项目的入侵尽可能的小
  • 跳转和跨模块功能的调用尽可能的显示调用
    • 跳转是不可能显示的,所以很多框架都会附带一个 IDEA Plugin 去帮助你把 跳转的地方和目标界面 联系在一起,达到一个 "显示" 调用
    • 跨模块功能的调用如何用的是服务发现,其实本身就是显示调用的
  • 配置尽可能的简单明了

当然还有我没有想到的,有兴趣的可以留言.以上的框架中我推荐使用

Component,它是基于 URI 设计的,并且比其他的基于 URI 设计的功能更全,更完善,也更好用.欢迎大家使用

以上是关于什么是 Android 组件化的主要内容,如果未能解决你的问题,请参考以下文章

[ 网络模型篇 ]大白话告诉你什么是OSI七层模型

Vue项目 UI框架介绍(第六天上)

白话文:微服务架构详解

大白话系统MySQL 学习总结 之 缓冲池(Buffer Pool) 的设计原理和管理机制

Android组件--碎片(fragment)

大白话Web三大组件之一Servlet