Android 架构 – 组件化 Componentization

参考文章

一、概念

组件化是基于可重用目的,对单个功能进行开发,提升复用性降低耦合度。多个功能组件起来就是一个业务组件,多个业务组件组合起来就是一个应用,因此去除了模块间的耦合,使得按业务划分的模块成了可单独运行的业务组件。(组件化只是一定程度上的独立,还是依附于整个项目中,想完全独立详见插件化)

  • 功能复用:每个组件都是一个单独的功能,可被不同的模块甚至不同的应用所使用。
  • 提高效率:每个组件都是一个可单独编译运行的APP,由于拆分使得代码量少,调试起来快。
  • 协同开发:组件之间相互独立,使得不同的开发人员能专注于各自的功能开发互不影响。

1.1 需要考虑的问题

组件分层、切换模式按需编译、组件通信(路由框架)、组件生命周期(组件在应用中存在的时间,组件是否可以按照需求动态使用,涉及到组件的加载卸载等管理问题。)

1.2 插件模式

在组件的构建脚本 build.gradle 中指定,指定为集成模式(Library)可以被其它组件调用,指定为组件模式(Application)可以独立运行调试。

com.android.application 项目构建后输出 apk 包,在调试时是一个应用能单独编译运行。
com.android.library 项目构建后输出 aar 包,在打包时是一个库文件集成到项目中。
com.android.test 配置一个 Android Test 工程。
//写法一(和别的一起写)
plugins {
    id 'com.android.library'
    id 'org.jetbrains.kotlin.android'
}
//写法二(分开写)
apply plugin: 'com.android.library'

1.3 创建组件

File→New→New Module。业务组件需要调试选择【Phone & Tablet】,功能组件和基础组件只用来集成,需要用到资源文件选【Android Library】,纯代码选【Java or kotlin Library】。

Android 架构 - 组件化 Componentization

二、结构划分

Android 架构 - 组件化 Componentization

依赖关系是上层依赖下层,修改频率是上层高于下层。对于业务组件由于存在页面跳转、方法调用、事件通信等问题需要使用路由通信,其它层组件不存在耦合问题封装成 Library 即可。

app壳工程 应用的入口:将业务组件打包成一个APP,做一些配置工作。(打包环境、签名、混淆等)
业务组件 某个页面(主页、消息、商城):业务组件之间无直接关系,通过路由进行通信。既可以作为 Application 单独编译运行调试,又可作为 Library 集成到项目中。
功能组件 公共功能(地图、分享、广告):是对公用的功能进行抽取,非必须层,业务组件可以直接依赖基础组件去实现功能。
基础组件 统一依赖配置和资源文件:引入第三方框架(Retrofit、Glide)、存放公用资源文件(strings、drawable、自定义View、工具类Utils)、实现路由(ARouter)。修改频率极低。

三、统一配置

3.1 模式切换

在 Program 的 build.gradle 中通过 ext{ } 添加全局变量 isDebug,用来切换业务组件的模式(true为组件模式会编译成Application,false为集成模式会编译成Library),同步一下后就可以在每个组件的 build.gradle 中读取并做相应的动态处理。(见下方5.5)

Android 架构 - 组件化 Componentization

3.2 规范一次引入多个组件

当需要同时引入好几个组件时,可以像“分组”一样一次性引入,避免每次新建组件引入多个其他组件的时候出错。像多渠道打包、测试某几个固定组件协同时使用。

Android 架构 - 组件化 Componentization​​​​

3.3 依赖管理 Verison Catalogs

此前会将依赖项定义在自定义的 config.gradle 中然后在 Program 的 build.gradle 中引入,这样不支持点击跳转和更新提示,且更新依赖会重构整个项目。

在项目上右键→New→File→命名为“libs.versions.toml”,然后在 settings.gradle 中的  标签下使用  引入所创建的文件。

versions 声明依赖项和插件的版本,然后引用这些变量。
libraries 声明依赖项的别名
bundles 声明依赖项的组名
plugins 声明插件

Android 架构 - 组件化 Componentization

3.4.1 依赖项

原来

dependencies {
    implementation 'androidx.core:core-ktx:1.9.0'
}

现在

[versions]
demo = "1.9.0"

[libraries]
#写法一
demo-compiler1 = "com.example:demo-compiler:1.9.0"
#写法二
demo-compiler2 = { module = "com.example:demo-compiler", version = "1.9.0" }
#写法三
demo-compiler3 = { module = "com.example:demo-compiler", version.ref = "1.9.0" }
#写法四
demo-compiler4 = { group = "com.example", name = "demo-compiler", version = "demo" }
#写法五
demo-compiler5 = { group = "com.example", name = "demo-compiler", version.ref = "demo" }
dependencies {
    #只有 libraries 中的属性可以直接调用
    #如果其它节点中有重名的,调用起来是 libs.versions.demo
    implementation libs.demo.compiler
}

3.4.2 插件

原来

// Program的build.gradle.kts中
plugins {
   id("com.android.application") version "7.4.1" apply false
}

// Module的build.gradle.kts中
plugins {
   id("com.android.application")
}

现在

[versions]
androidGradlePlugin = "7.4.1"

[plugins]
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
// Program的build.gradle.kts中
plugins {
   alias(libs.plugins.android.application) apply false
}

// Module的build.gradle.kts中
plugins {
   alias(libs.plugins.android.application)
}

3.4 抽取组件 build.gradle 中相同的配置

不同的 module 和 lib 组件都有很多重复的配置,可分别抽取成 “commonModule.gradle” 和 “commonLib.gradle” 然后在具体的模组件中引入使用,并删除已抽取的配置。

 在项目上右键→New→File→命名为“commonModule.gradle”。

  • 由于自定义的 gradle 文件中无法使用 plugins{ },因此需要将插件一个个引入。
  • commonModule:提供组件模式的切换,剔除各个 module 组件专有的 namespace、applicationId 属性。
  • commonLib:剔除各个 lib 组件专有的 namespace 属性。

Android 架构 - 组件化 Componentization

四、基础组件

将一直作为 Library 存在,app壳和所有的业务组件都依赖它,主要封装公共功能(网络请求、数据存储、自定义控件、工具类)、版本管理和统一第三方依赖。

4.1 创建 lib_common

Android 架构 - 组件化 Componentization

4.2 提供 BaseApplication

其它组件都依赖于 lib_common,因此在其中提供一个全局的 BaseApplication 供其它组件在代码中使用(需要获取全局上下文的时候就使用这个BaseApplication)。在集成模式(Library)中使用不需要注册,在App壳和组件模式(Application)中需要自定义一个类继承 BaseApplication 后注册到 AndroidManifest 中使得从 BaseApplication 中获取的全局上下文生效,还有初始化数据之用。

  • 对于集成模式(Library):直接使用 BaseApplication,不是应用不需要注册。
  • 对于app壳:创建一个子类继承自 BaseApplication 然后在 AndroidManifest 中注册。(不需要使用自己定义的就注册为 BaseApplication)
  • 对于组件模式(Application):在 debug 文件夹中创建一个子类继承自 BaseApplication 然后注册到 debug 文件夹中的 AndroidManifest 中。(不需要使用自己定义的就注册为 BaseApplication) 

Android 架构 - 组件化 Componentization

4.3 统一权限声明 AndroidManifest

声明了项目中用到的所有权限 user-permission 和 uses-feature,这样其它组件就无需在自己的 AndroidManifest.xm 声明自己要用到的权限了。

 Android 架构 - 组件化 Componentization

4.4 统一第三方依赖 build.gradle

        统一所有组件中用到的第三方库和jar包,具体写在 config.gradle 中再引入。使用 imlemntation 添加的依赖只对当前组件有效,使用 api 添加的依赖对其上层组件同样有效,这样依赖于基础组件的组件就不用再添加相同的依赖了。

        项目中某个组件会被其它组件重复依赖,在集成构建应用时 Gradle 会剔除重复的 arr 包,这样就不会存在好几份重复的代码了。而第三方库和我们的项目可能都依赖了相同的库(例如 Glide 中也依赖了 OkHttp)就会导致重复加载,解决办法是找出相同的库根据组件名或包名排除。

  • test替换不了,引入的时候提示无法识别(2024-01-04) 

Android 架构 - 组件化 Componentization

4.5 公共类

封装了项目中的 Base类、Utis工具类等,公用的 Widget控件、业务组件中都用到的数据也应放在这里(例如 SharedPreferences 和 DataBase 中的登录数据)。

4.6 公共资源

将公共的 drawable、shape、layout、strings、colors、dimens、styles、theme 等资源文件放在这里,提升复用性,保证主题统一、避免文件名冲突。

五、核心组件(业务组件同理)

5.1 创建 module_core

Android 架构 - 组件化 Componentization

5.2 创建 debug 包

在业务组件中创建 debug 包,用于存放只在组件模式下使用的文件(如程序入口 Launcher Activity、自定义Application),在集成模式下会被剔除不参与打包(见下方build.gradle中配置)。

在业务组件的 src/main/java 目录上右键→New→Package。(不推荐在组件的其它层级上创建目录存放,切换到Android视图不会显示)

Android 架构 - 组件化 Componentization

5.3 自定义的 Application

继承自 lib_common 中提供的 BaseApplication,组件自定义的 Application 写在 debug 目录中,在集成模式下剔除。(别忘了在下方 debug 目录中的 AndroidManifest 中注册)

Android 架构 - 组件化 Componentization

5.4 两份 AndroidManifest

        在组件模式下,也会因为标签配置、自定义Application 、指定启动入口(Launcher Activity)而在 AndroidManifest 中进行注册,但在集成模式下打包到一起时存在重复和冲突(桌面上好几个启动图标、只能注册一个自定义的Application、同一个权限无须多次申请)。

        由于可以在 build.gradle 中动态指定组件的模式,因此可以分别为两种模式加载不同的 AndroidManifest。组件模式下的 AndroidManifest 写在 debug 目录中,在集成模式下剔除。

  1. 将 AndroidStudio 为组件自动创建的 AndroidManifest 复制一份到 debug 目录,对  标签添加 package 属性指定为业务组件的包名。 像单工程那样使用就行。
  2. 修改自动创建的 AndroidManifest,将  的属性(保留了theme是方便组件中用到该主题的Activity不用每个都要单独设置) 删除。module_core 不要删除程序入口(app壳搬运的启动入口是这里),其它业务组件删除程序入口只保留四大组件的注册,对  标签添加 package 属性指定为组件的包名。

Android 架构 - 组件化 Componentization

5.5 修改 build.gradle

  1. 在组件模式下才是一个应用,因此需要动态配置组件模式和 applicationId。
  2. 在 标签下使用 动态指定不同模式下加载的 AndroidManifest 文件,并剔除 debug 包下的文件不参与集成模式下的打包。
  3. 将版本替换成 config.gradle 中定义的。
  4. 在  中引入依赖。

Android 架构 - 组件化 Componentization

5.6 程序入口 LauncherActivity

用于初始化数据后启动目标 Activity(不需要可以省略),因此写在 debug 目录下,不用 setContentView(),传入目标 Activity 需要的 Intent 参数即可。

Android 架构 - 组件化 Componentization

5.7 获取当前组件模式

如果想在代码中通过获取当前组件的模式进行特定开发,可以在业务组件 build.gradle 中的  标签下进行配置,会在 BuildConfig.java 类中生成对应的字段。

Android 架构 - 组件化 Componentization

5.8 专用资源重名问题 resourcePrefix

打包可能出现资源文件重名的问题(当前文件会覆盖被依赖模块中的),添加前缀来规范每个组件中专用资源文件的命名。

在 build.gradle 的  标签下添加 resourcePrefix “core_”,此后资源文件都会爆红提醒要以 “core_” 为前缀命名(不是报错,依旧能编译运行)。

Android 架构 - 组件化 Componentization

六、搬空app壳

组件化需要一个空壳,这个壳工程中不处理任何业务,也没有Activity,由它将各个业务模块组合起来构成一个完整的应用。

6.1 搬运程序入口 MainActivity

  1. 将 app 中的 MainActivity.class 和 activity_main.xml 剪切至 module_core 中。
  2. 将 AndroidManifest 中的 MainActivity 注册(包含程序入口)也剪切至 module_core 中只保留  标签。

Android 架构 - 组件化 Componentization

6.2 搬运资源文件 res

将 app 中的 整个 res 文件夹剪切至 lib_common 中。

6.3 自定义的 Application

必须继承自 lib_common 组件中的 BaseApplication(如果app壳工程中无需自定义的Application 可以直接在 AndroidManifest 中声明为 BaseApplication),因为只有这样在打包应用后才能让 BaseApplication 全局生效。

  • 可以在这里初始化项目中使用到的第三方SDK,还可以在这里解决 Android 引用方法数不能超过 65535 的限制,对崩溃事件的捕获和发送也可以在这里声明。 

Android 架构 - 组件化 Componentization

6.4 修改 AndroidManifest

标签的属性都是在app壳配置的,而其它组件也会有自己的清单,在集成模式下最终会打包合并成一个文件,需要解决属性重复(在上面5.4中用于集成模式的AndroidManifest的标签并不会进行配置,但还是会配置theme),分别对 app 的  和  添加如下代码。

manifest 标签 xmlns:tools=”http://schemas.android.com/tools”
application 标签 tools:replace=”android:name,android:label,android:icon,android:theme,android:allowBackup”

Android 架构 - 组件化 Componentization

6.5 修改 build.gradle

应用的打包签名、buildTypes、defaultConfig都需要在这里配置,而它的dependencies则需要根据 isDebug 的值分别依赖不同的组件,在组件模式下app壳只需要依赖 lib_common 组件,在集成模式下必须依赖所有声明的业务组件。

将  和  下的配置移动到 config.gradle 和 libs.versions.toml 中并引入,只在组件模式下引入业务组件。

Android 架构 - 组件化 Componentization

七、对比

app壳 Library Application
build.gradle 插件模式 com.android.application com.android.library com.android.application
applicationId 属性 组件模式下有,集成模式下无。
sourceSets 块 组件模式下调用 debug 包中的自定义 AndroidManifest,集成模式下调用 AndroidStudio 自动生成的并剔除 debug 包下的文件。
dependencies 块

组件模式下依赖所有业务组件,集成模式下只依赖基础组件。

基础组件添加所有组件用到的依赖,功能组件依赖基础组件。 依赖基础组件,看需求依赖公共业务组件。
AndroidManifest 标签 添加 tools 属性避免合并冲突。 设置 package 属性为自己包名。 设置 package 属性为自己包名。
标签 配置并添加 replace 属性避免合并冲突。 可以只配置theme属性避免每个Activity都要设置主题。 组件模式下配置,继承模式下可以只配置theme属性避免每个Activity都要设置主题。
自定义Application 继承 BaseApplication 提供 BaseApplication 继承 BaseApplication
启动入口 LauncherActivity 设为 MainActivity 设为 debug 包中的 LauncherActivity,内容为空用来做初始化后跳转到 业务Activity。
权限
四大组件 注册 注册
资源文件、公共类 基础组件存放都会用到的,功能组件存放自己专用的。 存放自己专用的

本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://net2asp.com/cca04e9974.html