文章目录

  • 什么是组件化
  • 模块化工程
  • 组件化工程
  • 组件化实施
  • 总体流程
  • 组件模式和集成模式
  • AndroidManifest.xml合并冲突
  • 组件数据初始化
  • 组件化通信
  • 组件化最佳实践
  • 类型划分
  • 统一配置
  • 组件化混淆
  • 组件化Demo
  • 参考文档


什么是组件化

模块化工程

在引入组件化之前,我们开发的APP工程架构模型基本上是这样的:

android组件化数据 android 组件化_android组件化数据


各个业务关联同一个/多个依赖库(模块),模块中封装常用的业务共用流程、网络请求、数据操作等,业务逻辑通过文件夹划分,且业务之间高度耦合,我中有你,你中有我

android组件化数据 android 组件化_android组件化数据_02


这种单一的工程模块在业务功能简单且变动不大的情况下,能快速进行迭代开发,满足产品需求。但当应用业务越来越复杂,产品需求频繁变化的情况下,就会暴露出一些问题:

  1. 修改某个业务,因耦合度高,导致其他业务受影响
  2. 对工程所做的任何修改都要编译整个工程
  3. 每次修改,都需要覆盖所有业务测试
  4. 当多人进行协同开发时,业务的修改需同步到所有人
  5. 工程会越来越臃肿,后续维护成本高

组件化工程

android组件化数据 android 组件化_组件化_03


当应用越来越复杂,为了解决模块化工程存在的种种问题,越来越多地应用开始引入组件化工程的架构,我们来一起看下它是如何解决这些问题的。

  1. 工程下的各个业务组件是独立的,没有关联,这意味着对其中一个业务组件进行修改,不影响其他业务。
  2. 各个业务组件,既可以单独编译运行,也可以集成到app工程中运行。当对单个业务进行修改时,只需要单独编译运行,从而提高编译效率
  3. 若业务间通信接口未更改,则只需要对修改的业务组件单独进行测试即可。
  4. 多人协同开发,只需先定好组件通信的接口,剩余的开发和修改不会相互影响,提高开发效率
  5. app工程可根据实际情况,自由加载需要的业务组件,降低后期维护成本

除了能解决以上这些问题外,组件化还能够带来其他一些好处:

  1. 代码权限控制可精细到组件,更好地做代码管控
  2. 不需要熟悉所有业务,使新员工能更快上手

组件化实施

总体流程

  1. 根据APP开发的功能,对业务进行划分,得到多个业务组件,然后再对各个业务组件进行分析,抽离出公共的功能点,划分出多个功能模块。

业务组件和功能模块的不同点在于前者可单独编译运行,后者不行

  1. 定义好业务通信的接口,选用三方或者定制组件间的通信方案。
  2. 各个组件功能开发联调及测试。
  3. 将各个组件集成到同一个app,联调测试。

组件模式和集成模式

  • 当划分好业务组件,需要对组件功能进行单独调试时,需使用组件模式
  • 而组件功能已基本开发联调完成,需要集成到APP中测试时,需使用集成模式

组件模式和集成模式可在module中的build.gradle中进行设置:

//组件模式,设置为application属性,可单独运行
apply plugin: ‘com.android.application’

//集成模式,设置为libaray属性,不可单独运行
apply plugin: ‘com.android.library’

因组件开发经常需要在两种模式之间进行切换,如果每次切换都去修改每个module下的属性,就很会影响开发效率,需要提供一个自动切换的方案。
好在app下的gradle.properties可协助实现此功能,在该文件下配置的常量属性,可以在工程中的任意build.grade文件中读取。

首先我们在gradle.properties文件中定义一个常量值 isModule(是否是组件开发模式,true为是,false为否)

# 每次更改“isModule”的值后,需要点击 "Sync Project" 按钮
isModule=false

然后在业务组件中读取该值,并设置为对应的属性,从而实现自动切换

if (isModule.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

AndroidManifest.xml合并冲突

每一个业务组件都会关联一个AndroidManifest.xml,每个AndroidManifest.xml在组件开发模式下,都会包含一个app和launch activity。

组件单独运行时正常,但在集成模式下合并时,因存在多个app会导致冲突

这里的解决思路类似于上个段落,不同模式加载不同的xml文件,组件模式下加载带app的xml文件,集成模式下加载不带app的文件。

android组件化数据 android 组件化_android_04


同样地,在业务组件中指定对应AndroidManifest.xml文件的位置,解决合并冲突问题

sourceSets {
        main {
            if (isModule.toBoolean()) {
                manifest.srcFile 'src/main/module/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
            }
        }
    }

组件数据初始化

当使用组件模式进行开发,需要获取应用上下文context及初始化数据,但由于应用上下文context是全局的,只能有一个,当多个组件进行合并的,如何共用一个context就是个问题。

这里我们可以建立一个common模块,用于封装项目常用的基础功能,每个业务组件都依赖它,并在其中创建一个BaseApplication类,用于应用初始化时,通过继承此类来获取context以及做组件的初始化工作。

android组件化数据 android 组件化_组件化入门_05


通用NewApplication继承BaseApplication类,并在其中做组件的初始化工作,同时也可以在文件夹中增加组件测试用Activity,传递测试参数,如下所示:

public class LauncherActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        request();
        Intent intent = new Intent(this, TargetActivity.class);
        intent.putExtra("name", "avcd");
        intent.putExtra("syscode", "023e2e12ed");
        startActivity(intent);
        finish();
    }

    //申请读写权限
    private void request() {
        AndPermission.with(this)
                .requestCode(110)
                .permission(Manifest.permission.WRITE_EXTERNAL_STORAGE,
                        Manifest.permission.CAMERA, Manifest.permission.READ_PHONE_STATE)
                .callback(this)
                .start();
    }

}

接下来在业务组件的 build.gradle 中,根据 isModule 是否是集成模式将 debug 这个 Java代码文件夹排除:

sourceSets {
        main {
            if (isModule.toBoolean()) {
                manifest.srcFile 'src/main/module/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
                //集成开发模式下排除debug文件夹中的所有Java文件
                java {
                    exclude 'debug/**'
                }
            }
        }
    }

组件化通信

前面我们解决了单独开发组件存在的模块切换以及文件冲突、数据初始化等问题,组件化还少不了需要互相通信,那如何在不相互依赖的情况,进行通信呢?

这里可以借鉴初始化数据的思想,将组件化通信统一通过三方进行处理,如下图所示:

android组件化数据 android 组件化_android_06


各个组件通过统一的路由进行转发通信,实现组件解耦的目的。

组件间常见的通信场景包括:

  1. Activity的跳转
  2. Fragment的交互
  3. 组件服务接口的调用
  4. 支持跳转携带参数,支持startActivityForResult
  5. 跳转失败降级

网上有很多开源的路由通信引擎,可以满足组件化通信,常见的包括ARouter、CC、得到DDComponentForAndroid等
各开源引擎对比:https://github.com/luckybilly/AndroidComponentizeLibs 可根据APP开发实际情况选择合适的路由通信引擎,此处选取ARouter作为组件化通信的例子来说明

ARouter是一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦。
具体使用说明可参见ARouter github地址

组件化最佳实践

类型划分

可将组件划分为不同类型,每个类型承担不同的职责,使得组件化工程标准化。

  1. common模块

各个组件共用的模块,用于封装一些基础功能、公用功能,可将组件共用的依赖库声明、工程权限声明放置在该模块中,方便组件使用

  1. 业务组件

承担具体的业务功能,业务组件的划分标准是功能独立可复用,声明isModule变量,用于集成模式和开发模式切换

  1. main组件

业务入口,一般启动页、欢迎页会放置在此组件中,通过此组件可跳转到业务组件

  1. app组件

app壳工程,没有具体的业务,一般把工程包名、版本号、应用图标及应用打包相关参数放置在此

统一配置

不同开发人员使用的电脑配置都是不一样的,这就导致了工程导入时经常因为版本的问题需要重新调试编译,导致浪费了很多时间
我们可以约定在工程的根目录的build.gradle进行统一配置,当需要移植时,只需更改该文件即可,如下所示:

// Define versions in a single place
//时间:2017.11.28;每次修改版本号都要添加修改时间
ext {
    // Sdk and tools
    //localBuildToolsVersion是gradle.properties中的数据
    buildToolsVersion = localBuildToolsVersion
    compileSdkVersion = 26
    minSdkVersion = 17
    targetSdkVersion = 26
    versionCode = 1
    versionName = "1.0"
    javaVersion = JavaVersion.VERSION_1_8

    // App dependencies version
    supportLibraryVersion = "26.+"
    retrofitVersion = "2.1.0"
    glideVersion = "3.7.0"
    loggerVersion = "1.15"
    eventbusVersion = "3.0.0"
    gsonVersion = "2.8.0"
    photoViewVersion = "2.0.0"

    //检查时间:2017.6.6
    annotationProcessor = "1.0.3"
    routerVersion = "1.2.1.1"
    easyRecyclerVersion = "4.4.0"
    cookieVersion = "v1.0.1"
    toastyVersion = "1.1.3"

gradle的buildToolVersion和gradlePluginVersion可配置在gradle.properties中

# 为自动化出包配置(因为每个开发的电脑坏境不一致)
localBuildToolsVersion=26.0.2
# 这个值一般跟你的AndroidStudio版本号一致
localGradlePluginVersion=2.3.3

组件化混淆

一般情况下可直接在app组件中配置混淆,应用打包发布也都是在这个组件中,这么有几个好处:

  1. 在app组件中混淆,其余组件不混淆,方便根据日志定位问题原因
  2. 只生成一份混淆配置文件,便于修改和管理

组件化Demo

根据本篇组件化思想,开发了一个入门的组件化实例,供各位参考。

参考文档