赞
踩
组件化是基于可重用目的,对单个功能进行开发,提升复用性降低耦合度。多个功能组件起来就是一个业务组件,多个业务组件组合起来就是一个应用,因此去除了模块间的耦合,使得按业务划分的模块成了可单独运行的业务组件。(组件化只是一定程度上的独立,还是依附于整个项目中,想完全独立详见插件化)
组件分层、切换模式按需编译、组件通信(路由框架)、组件生命周期(组件在应用中存在的时间,组件是否可以按照需求动态使用,涉及到组件的加载卸载等管理问题。)
在组件的构建脚本 build.gradle 中指定,指定为集成模式(Library)可以被其它组件调用,指定为组件模式(Application)可以独立运行调试。
com.android.application | 项目构建后输出 apk 包,在调试时是一个应用能单独编译运行。 |
com.android.library | 项目构建后输出 aar 包,在打包时是一个库文件集成到项目中。 |
- //写法一(和别的一起写)
- plugins {
- id 'com.android.library'
- id 'org.jetbrains.kotlin.android'
- }
- //写法二(分开写)
- apply plugin: 'com.android.library'
File→New→New Module。业务组件需要调试选择【Phone & Tablet】,功能组件和基础组件只用来集成,需要用到资源文件选【Android Library】,纯代码选【Java or kotlin Library】。
依赖关系是上层依赖下层,修改频率是上层高于下层。对于业务组件由于存在页面跳转、方法调用、事件通信等问题需要使用路由通信,其它层组件不存在耦合问题封装成 Library 即可。
app壳工程 | 应用的入口:将业务组件打包成一个APP,做一些配置工作。(打包环境、签名、混淆等) |
业务组件 | 某个页面(主页、消息、商城):业务组件之间无直接关系,通过路由进行通信。既可以作为 Application 单独编译运行调试,又可作为 Library 集成到项目中。 |
功能组件 | 公共功能(地图、分享、广告):是对公用的功能进行抽取,非必须层,业务组件可以直接依赖基础组件去实现功能。 |
基础组件 | 统一依赖配置和资源文件:引入第三方框架(Retrofit、Glide)、存放公用资源文件(strings、drawable、自定义View、工具类Utils)、实现路由(ARouter)。修改频率极低。 |
在 Program 的 build.gradle 中通过 ext{ } 添加全局变量 isDebug,用来切换业务组件的模式(true为组件模式会编译成Application,false为集成模式会编译成Library),同步一下后就可以在每个组件的 build.gradle 中读取并做相应的动态处理。(见下方5.5)
此前会将依赖项定义在自定义的 config.gradle 中然后在 Program 的 build.gradle 中引入,这样不支持点击跳转和更新提示,且更新依赖会重构整个项目。
在项目上右键→New→File→命名为“libs.versions.toml”,然后在 settings.gradle 中的 <dependencyResolutionManagement> 标签下使用 <versionCatalogs> 引入所创建的文件。
versions | 声明依赖项和插件的版本,然后引用这些变量。 |
libraries | 声明依赖项的别名 |
bundles | 声明依赖项的组名 |
plugins | 声明插件 |
原来
- 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
- }
原来
- // Program的build.gradle.kts中
- plugins {
- id("com.android.application") version "7.4.1" apply false
- }
-
- // Module的build.gradle.kts中
- plugins {
- id("com.android.application") //聚合写法
- }
- apply plugin: 'com.android.library' //单个写法
现在
- [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) //聚合写法
- }
- apply plugin: libs.plugins.android.library //单个写法
AGP 升级助手无法识别 toml,将 Project 的 build.gradle 中 application 和 library 还原成默认书写方式。
- id 'com.android.application' version '8.3.0' apply false
- id 'com.android.library' version '8.3.0' apply false
不同的 module 和 lib 组件都有很多重复的配置,可分别抽取成 “commonModule.gradle” 和 “commonLib.gradle” 然后在具体的模组件中引入使用,并删除已抽取的配置。
在项目上右键→New→File→命名为“commonModule.gradle”。
将一直作为 Library 存在,app壳和所有的业务组件都依赖它,主要封装公共功能(网络请求、数据存储、自定义控件、工具类)、版本管理和统一第三方依赖。
其它组件都依赖于 lib_common,因此在其中提供一个全局的 BaseApplication 供其它组件在代码中使用(需要获取全局上下文的时候就使用这个BaseApplication)。在集成模式(Library)中使用不需要注册,在App壳和组件模式(Application)中需要自定义一个类继承 BaseApplication 后注册到 AndroidManifest 中使得从 BaseApplication 中获取的全局上下文生效,还有初始化数据之用。
声明了项目中用到的所有权限 user-permission 和 uses-feature,这样其它组件就无需在自己的 AndroidManifest.xm 声明自己要用到的权限了。
统一所有组件中用到的第三方库和jar包,具体写在 config.gradle 中再引入。使用 imlemntation 添加的依赖只对当前组件有效,使用 api 添加的依赖对其上层组件同样有效,这样依赖于基础组件的组件就不用再添加相同的依赖了。
项目中某个组件会被其它组件重复依赖,在集成构建应用时 Gradle 会剔除重复的 arr 包,这样就不会存在好几份重复的代码了。而第三方库和我们的项目可能都依赖了相同的库(例如 Glide 中也依赖了 OkHttp)就会导致重复加载,解决办法是找出相同的库根据组件名或包名排除。
封装了项目中的 Base类、Utis工具类等,公用的 Widget控件、业务组件中都用到的数据也应放在这里(例如 SharedPreferences 和 DataBase 中的登录数据)。
将公共的 drawable、shape、layout、strings、colors、dimens、styles、theme 等资源文件放在这里,提升复用性,保证主题统一、避免文件名冲突。
在业务组件中创建 debug 包,用于存放只在组件模式下使用的文件(如程序入口 Launcher Activity、自定义Application),在集成模式下会被剔除不参与打包(见下方build.gradle中配置)。
在业务组件的 src/main/java 目录上右键→New→Package。(不推荐在组件的其它层级上创建目录存放,切换到Android视图不会显示)
继承自 lib_common 中提供的 BaseApplication,组件自定义的 Application 写在 debug 目录中,在集成模式下剔除。(别忘了在下方 debug 目录中的 AndroidManifest 中注册)
在组件模式下,也会因为<application>标签配置、自定义Application 、指定启动入口(Launcher Activity)而在 AndroidManifest 中进行注册,但在集成模式下打包到一起时存在重复和冲突(桌面上好几个启动图标、只能注册一个自定义的Application、同一个权限无须多次申请)。
由于可以在 build.gradle 中动态指定组件的模式,因此可以分别为两种模式加载不同的 AndroidManifest。组件模式下的 AndroidManifest 写在 debug 目录中,在集成模式下剔除。
用于初始化数据后启动目标 Activity(不需要可以省略),因此写在 debug 目录下,不用 setContentView(),传入目标 Activity 需要的 Intent 参数即可。
如果想在代码中通过获取当前组件的模式进行特定开发,可以在业务组件 build.gradle 中的 <defaultConfig> 标签下进行配置,会在 BuildConfig.java 类中生成对应的字段。
打包可能出现资源文件重名的问题(当前文件会覆盖被依赖模块中的),添加前缀来规范每个组件中专用资源文件的命名。
在 build.gradle 的 <android> 标签下添加 resourcePrefix "core_",此后资源文件都会爆红提醒要以 “core_” 为前缀命名(不是报错,依旧能编译运行)。
组件化需要一个空壳,这个壳工程中不处理任何业务,也没有Activity,由它将各个业务模块组合起来构成一个完整的应用。
将 app 中的 整个 res 文件夹剪切至 lib_common 中。
必须继承自 lib_common 组件中的 BaseApplication(如果app壳工程中无需自定义的Application 可以直接在 AndroidManifest 中声明为 BaseApplication),因为只有这样在打包应用后才能让 BaseApplication 全局生效。
<application>标签的属性都是在app壳配置的,而其它组件也会有自己的清单,在集成模式下最终会打包合并成一个文件,需要解决属性重复(在上面5.4中用于集成模式的AndroidManifest的<application>标签并不会进行配置,但还是会配置theme),分别对 app 的 <manifest> 和 <application> 添加如下代码。
manifest 标签 | xmlns:tools="http://schemas.android.com/tools" |
application 标签 | tools:replace="android:name,android:label,android:icon,android:theme,android:allowBackup" |
应用的打包签名、buildTypes、defaultConfig都需要在这里配置,而它的dependencies则需要根据 isDebug 的值分别依赖不同的组件,在组件模式下app壳只需要依赖 lib_common 组件,在集成模式下必须依赖所有声明的业务组件。当需要同时引入好几个组件时,可以像“分组”一样一次性引入,避免每次新建组件引入多个其他组件的时候出错。像多渠道打包、测试某几个固定组件协同时使用。
将 <defaultConfig> 和 <dependencies> 下的配置移动 libs.versions.toml 中并引入,只在组件模式下引入业务组件。
app壳 | Library | Application | ||
build.gradle | 插件模式 | com.android.application | com.android.library | com.android.application |
applicationId 属性 | 有 | 无 | 组件模式下有,集成模式下无。 | |
sourceSets 块 | 无 | 无 | 组件模式下调用 debug 包中的自定义 AndroidManifest,集成模式下调用 AndroidStudio 自动生成的并剔除 debug 包下的文件。 | |
dependencies 块 | 组件模式下依赖所有业务组件,集成模式下只依赖基础组件。 | 基础组件添加所有组件用到的依赖,功能组件依赖基础组件。 | 依赖基础组件,看需求依赖公共业务组件。 | |
AndroidManifest | <manifest> 标签 | 添加 tools 属性避免合并冲突。 | 设置 package 属性为自己包名。 | 设置 package 属性为自己包名。 |
<application> 标签 | 配置并添加 replace 属性避免合并冲突。 | 可以只配置theme属性避免每个Activity都要设置主题。 | 组件模式下配置,继承模式下可以只配置theme属性避免每个Activity都要设置主题。 | |
自定义Application | 继承 BaseApplication | 提供 BaseApplication | 继承 BaseApplication | |
启动入口 LauncherActivity | 无 | 设为 MainActivity | 设为 debug 包中的 LauncherActivity,内容为空用来做初始化后跳转到 业务Activity。 | |
权限 | 无 | |||
四大组件 | 无 | 注册 | 注册 | |
资源文件、公共类 | 无 | 基础组件存放都会用到的,功能组件存放自己专用的。 | 存放自己专用的 |
组件化后使得代码隔离,访问不到其它组件中的代码,通过接口解耦的方式,调用方不需要关心服务是谁提供的,提供方不需要关心谁在何时调用服务。对于接口文件的存放地需要考虑是否去中心化。
统一存放(小型项目) | 分开存放(大型项目) | |
优点 | 管理方便 | 每个接口可使用不同的版本并且使用方只需要依赖特定的接口。 |
缺点 | 不利于需要跨APP复用的项目(所有接口对应一个组件版本,不能支持单一接口使用不同版本的组件),并且使用方可能会引入大量无用的接口依赖。 | 会创建更多的组件,组件数量会增加依赖查找的耗时。 |
实现 | 可以用一个专门的组件 lib_router 来存放所有组件路由接口来避免 lib_common 变得臃肿,每个组件都依赖该模块实现自己业务的接口并提供路由。 | module_login 对应有一个 export_login,使用一个组件都要添加两个依赖。可以把 export_login 当作 module_login 组件的协议,通过它可以知道能和 module_login 做些什么交互(包含了服务接口和路由表),减少协同开发沟通成本。 |
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。