Toc
  1. 为什么采用组件化
    1. 单一工程模式开发的痛点
    2. 组件化开发的优点
  2. 组件化架构设计
  3. 组件间的通信问题
    1. 页面跳转
    2. 数据传递
    3. 数据共享、事件响应
  4. 组件的生命周期管理
    1. 组件初始化的顺序
Toc
0 results found
关于组件化

为什么采用组件化

我们从两个角度来分析这个问题。

单一工程模式开发的痛点

  1. 对工程的任意修改调试都要编译整个工程,效率十分低下。

    做 APP 开发时,我们需要经常在手机或模拟器上进行调试,而每次调试都需要对整个工程进行编译,然后安装在手机上运行。即便你只是改了一句代码,或是 UI 调整了一个像素点,同样需要完整的编译工程。当工程代码越来越多时,编译也会越来越慢,你可以想象一下我修改了某句代码,编译一下需要等待 4、5 分钟才能成功运行的场景么,那简直让人崩溃,严重影响了开发效率(想起曾经用 eclipse 开发 Android 时,各种转菊花,卡顿得让人想死的心都有)。

  2. 不利于多人团队协同开发。

    早期一个 APP 可能就 1、2 个人来开发,但是随着业务的扩张,我们可能会发展到一个团队来开发一个 APP,少则 4、5 个人,多则 10 几个人甚至更多。像手机淘宝、微信、支付宝这些巨无霸 APP,他们的 APP 开发人员估计起码有数百个。

    以 10 人团队为例,如果 10 个人都是基于同一个工程的代码拉分支进行开发,每人的开发任务虽然不同,但是都能修改整个工程的任意地方。为了适应自己的需求,团队内某人改了某句代码,但是这个改动又有可能影响别人的开发,这样开发人员之间势必要花更多的时间去沟通和协调,没法专注自己的功能点。最后进行 10 个人的代码合并时,有过这方面经历的人,就知道这是一件多么痛苦的事情,解决冲突解决得你要怀疑人生。

  3. 无法做到功能复用。

    我曾经做过一个项目,每个开通这个业务的城市,都要做一个单独的 APP,初期我们只开通了 3、4 个城市,需要同时发布 3、4 个 APP,这些 APP 大概 6、70% 功能是相同的,但是都需要加入地方定制功能。如果你每个 APP 都采用单一工程模式开发,刚开始你可能每个工程都有同样的代码存在,只需要复制拷贝一下就行,但是如果有需求要对这些进行修改时,你必须得每个工程都逐一修改一遍,然后每个 APP 都测试一遍,工作量直接翻了数倍。

  4. 业务模块间耦合严重。

    采用单一工程模式开发项目,到最后势必会造成业务模块高度耦合,可以说是你中有我、我中有你,修改任何业务都有可能导致牵一发而动全身,这显然是不利于后期项目功能维护以及迭代开发的。

组件化开发的优点

  1. 极大提高工程编译速度。

    进行组件化拆分后,每个业务或者功能都是一个单独的工程,这个单独的工程可以独立编译运行,拆分后的工程通常都比较小,代码量也比较少,我再也不用像以前编译一下得等待好几分钟了。

  2. 业务模块解耦,有利于多人团队协作开发。

    业务组件之间不能相互引用,每个组件都把对应的业务功能收敛在一个工程里,彼此互不打扰。 在多人团队里,每个人只负责自己的业务模块,他对业务功能的增删改查,都只限定在自己的这个业务模块里,不会影响其他人的业务,他代码质量的好坏也只会影响到自己的业务模块;对测试来说,也十分方便,大部分情况下,我们只需要着重测试修改过的业务组件即可,而不用老是进行全部回归测试。

  3. 组件化是功能重用的基石。

    业务组件类似一个个积木一样,我们可以用积木搭建出不同的房子,同理我们也可以创建多个不同的 APP。我们只需要维护好每个组件,需要用到该组件的功能时,一建引用集成就可以了。

当然,这并不是说组件化开发只有优点没有缺点,例如:

  • 组件化开发前期可能要花费更多的时间来进行模块拆分;
  • 一个人的小项目完全没必要组件化开发,那样只会给自己带来更多的工作量;
  • 组件化可能会带来更多重复的代码;
  • 组件化需要良好的架构设计,包括怎么拆分业务,组件之间怎么通信等等,需要有个高水平的架构师统筹全局,经验不足的同学盲目进行组件化反而会适得其反,带来更多的麻烦。

组件化架构设计

在进行组件化开发之前,一定要设计好构架方案,一般来说,会分为至少 4 层:APP 壳工程、常规业务组件层、基础 / 公共业务组件层和基础功能组件层。

下面一个图示意了一个工程的典型分层:

我们来从下往上分析一下这样分层的思路。

  • 基础功能组件层

    这一层的功能都是最基础的功能,通常不包含任何业务逻辑,也可以说这些组件是一些通用的工具类。例如日志记录组件,它只是提供了日志记录的能力,你要记录什么样的日志,它并不关心;例如基础 UI 组件,它是一个全局通用的 UI 资源库;例如网络服务组件,它封装了网络的请求能力,等等。

  • 基础业务组件层

    这一层层组件是对一些系统通用的业务能力进行封装的组件。例如公共业务组件里,可以封装 BaseActivity、BaseFragment 等;例如分享能力组件,我封装了微信、QQ、微博等的分享能力,其他业务只要集成该组件就能进行相关分享;例如共享公共数据组件,可以封装应用能够全局访问的数据,如用户登录信息等。

  • 常规业务组件

    该层的组件就是我们真正的业务组件了。我们通常按照功能模块来划分业务组件,例如注册登录、用户个人中心、APP 的首页模块等。这里的每个业务组件都是一个小的 APP,它必须可以单独编译,单独打包成 APK 在手机上运行。

  • 核心管理组件

    主要包括路由服务组件、组件生命周期管理组件。路由主要是为了解决组件间通信问题,而组件生命周期管理主要是为了解决组件的初始化等问题。

  • APP 壳工程

    壳工程依赖了需要集成的业务组件,它可能只有一些配置文件,没有任何代码逻辑。根据你的需要选择集成你的业务组件,不同的业务组件就组成了不同的 APP。

在组件之间必须遵循以下规则:

  • 只能上层组件引用下层组件,不能反向依赖,否则可能会出现循环依赖的情况
  • 同一层组件之前不能相互依赖,这是为了组件间彻底解耦合

组件间的通信问题

在组件化实施的过程中,必然会遇到几个问题,比如:

怎么从这个组件跳转到另一个组件的页面
组件之间怎么传递数据
怎么获取其他组件的数据或服务
组件怎么通知其他组件响应某个事件

这些问题可以统统定义为:组件之间的通信问题。

我们来按照使用场景来分析一下。

页面跳转

在单一工程中,页面跳转可以直接用 startActivity() 方法跳转页面。但是组件化之后,同一组件层的组件是不能相互依赖的,也就是说无法再通过 startActivity() 来进行跳转了。这就必须要引用一个新的思路:页面路由。

路由框架现在成熟的有很多了,比如 美团的 WMRouter 阿里的 ARouter

路由框架的核心原理都是一样的,我们来简单分析一下。

实现思路是专门抽取一个 Module 作为路由服务,每个组件声明自己提供的服务 Service API,这些 Service 都是一些接口,组件负责将这些 Service 实现并注册到一个统一的路由 Router 中去,如果要使用某个组件的功能,只需要向 Router 请求这个 Service 的实现,具体的实现细节我们全然不关心,只要能返回我们需要的结果就可以了。看起来是不是有点像 Flutter 的路由?

数据传递

有了路由系统,数据传递自然也可以交给路由系统。举个例子:

我有一个路由,URI 为:

myscheme://custompath/pageA?param1=*¶m2=*

param1 和 param2 自然就可以做为参数传入目标组件。

数据共享、事件响应

数据共享和事件响应可以采用 EventBus 来实现,但是 EventBus 在组件化开发时,一定要注意事件名的命名,组件事件名最好有规范的定义方式,避免不同人员开发时出现冲突,日后不好维护。

当然如 美团的 WMRouter也有比较成熟的 ServiceLoader 的技术方案,提供服务共享、数据共享。

组件的生命周期管理

每个 Android 应用启动时,都会先创建一个 Application。通常在 Application 里我们会做一些应用初始化的操作,常见的有第三方 SDK 初始化。在应用组件化之后,组件与壳工程是隔离开来的,但是组件有时候也需要获取应用的 Application,也需要在应用启动时进行初始化。这就涉及到组件的生命周期管理问题。

  1. 抽象出一个类似 Application 的类,它模拟了 Application 的几个主要方法:
public abstract class BaseAppLike {

public static final int MAX_PRIORITY = 10;
public static final int MIN_PRIORITY = 1;
public static final int NORM_PRIORITY = 5;

/**
* 返回组件的优先级,优先级范围为[1-10],10 为最高,1 为最低,默认优先级是 5
*
* @return
*/
public int getPriority() {
return NORM_PRIORITY;
}

/**
* 应用初始化
*
* @param context
*/
public abstract void onCreate(Context context);

public abstract void onTerminate();
}

而组件内部都要实现一个继承 BaseAppLike 的类,并将其当做 Application 的容器。组件可以在这里获取 Application 实例、执行启动时的初始化等等。

这时我们假设有三个组件 ModuleA、ModuleB、ModuleC,这三个组件内部分别有 ModuleAAppLike、ModuleBAppLike、ModuleCAppLike,那么在壳工程中,初始化时可以这样做:

class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
ModuleAAppLike moduleA = new ModuleAAppLike();
ModuleBAppLike moduleB = new ModuleBAppLike();
ModuleCAppLike moduleC = new ModuleCAppLike();
moduleA.onCreate(this);
moduleB.onCreate(this);
moduleC.onCreate(this);
}
}

这样会不会有问题?有。

组件初始化的顺序

前面介绍过,上层业务组件是依赖下层业务组件的,如果下层组件在应用启动时也需要初始化,那么我们在加载组件时,必然要先加载下层组件,否则加载上层组件时可能会出现问题。但是组件这么多,我们怎么确定要先加载谁后加载谁呢,当然你可以手动维护,代码里写死,但是当业务越来越多、时间越来越久,肯定不灵活,你新加一个业务组件进来,你都需要确定组件初始化先后顺序。所以,我们必须有个机制来确定组件初始化先后顺序。

类似线程优先级一样, 为每个组件定义了一个优先级,通过重写 getPriority() 方法可以设置组件的优先级。优先级范围从[1-10],默认优先级都为 5,下层组件或需要先初始化的组件,优先级设置高一点。这样我们在加载组件的时候,先对所有组件的优先级进行排序,优先级高的排前面,然后再按顺序进行加载组件,就可解决这个问题了。

打赏
支付宝
微信
本文作者:CodingRabbit
版权声明:本文首发于CodingRabbit的博客,转载请注明出处!