组件化的目的
单工程项目缺陷
- 1.各种业务代码在一个模块里,定位问题需要在多个业务混合的模块中找
- 2.每个开发都要了解各个模块的业务,不然小的改动可能影响到其他业务
- 3.编译代码的时间随着工程变大而变长
- 4.多人协作开发,开发风格不一,业务又无法分割,互相影响,效率低下
- 5.代码复用性差,写过的代码很难复用
组件化的好处
组件化基于可重用的目的,拆分为独立组件,减少耦合
- 1.分为多个模块,每个模块一个组件,业务分离
- 2.组件可以单独运行,解决编译耗时问题
- 3.多人开发,每个人负责自己的模块,降低开发人员沟通成本,减少因为代码风格不一而产生的影响
- 4.哪个组件坏了系统仍可继续运行,低耦合高内聚
模块化与组件化的区别?
模块化就是将功能拆分为独立的模块,比如登录模块,消息模块
组件化比模块化粒度更小,一个模块包含一个或者多个组件
插件化与组件化的区别?
插件化也是将功能拆分为独立的模块,区别是插件化的模块都是一个个APK,打包时将宿主APK和插件APK分开打包,程序运行时,宿主APK可以动态的选择加载插件APK
- 组件化在调试的时候可以切换为application,打包的时候确还是library,应用只有一个apk
- 插件化是拆分出多个apk,运行的时候通过动态加载来加载这些apk
组件化要考虑的问题?
组件分层 ,组件单独运行和调试,组件之间通信,组件生命周期
可参考如下组件分层
下面通过代码快速简单搭建一个
先看一下结构分层:settings.gradle
app:主工程
bss:业务组件层
func:功能组件层
base:基础组件
rootProject.name = "zkt"
include ':app'
include ':bss:trade'
include ':bss:zkp'
include ':func:pay'
include ':func:share'
include ':base:libBase'
include ':base:libRouter'
接下来看看各个build.gradle配置
最外层project下的build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = "1.3.72"
repositories {
google()
jcenter()
mavenCentral()
maven { url "https://jitpack.io" }
maven { url 'https://repo1.maven.org/maven2/' }
}
dependencies {
classpath 'com.android.tools.build:gradle:7.0.4'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
mavenCentral()
maven { url "https://jitpack.io" }
maven { url 'https://repo1.maven.org/maven2/' }
flatDir {
dirs 'libs'
dirs project(':base:libBase').file('libs')
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
//-----------------------------------------自定义配置---------------------------------------------
ext {
app = "${rootDir}/app.gradle"
lib = "${rootDir}/lib.gradle"
bss = "${rootDir}/bss.gradle"
//true是调试模式,组件可以单独运行,false是正式打包模式
//运行app的时候要把这里设置为false
isDebug = false
android = [
compileSdk : 31,
minSdk : 23,
targetSdk : 31,
versionCode: 10000,
versionName: "1.0.0"
]
applicationId = [
"app": "com.k.zkt",
"trade": "com.k.trade",
"zkp": "com.k.zkp"
]
//android中的lib
library = [
"corektx" : "androidx.core:core-ktx:1.7.0",
"appcompat" : "androidx.appcompat:appcompat:1.3.0",
"material" : "com.google.android.material:material:1.4.0",
"constraintlayout": "androidx.constraintlayout:constraintlayout:2.0.4",
"junit" : "junit:junit:4.13.2",
"junitext" : "androidx.test.ext:junit:1.1.3",
"espressocore" : "androidx.test.espresso:espresso-core:3.4.0",
]
//第三方
arouter = "com.alibaba:arouter-api:1.5.2"
//哪个业务模块用到arouterCompiler,都需要单独依赖和添加AROUTER_MODULE_NAME
arouterCompiler = "com.alibaba:arouter-compiler:1.5.2"
litepal = "org.litepal.android:kotlin:3.0.0"
gson = "com.google.code.gson:gson:2.8.6"
mmkv = "com.tencent:mmkv-static:1.2.10"
okhttp = "com.squareup.okhttp3:okhttp:3.14.9"
retrofit = "com.squareup.retrofit2:retrofit:2.8.2"
koinScope = "org.koin:koin-androidx-scope:2.1.5"
koinViewmodel = "org.koin:koin-androidx-viewmodel:2.1.5"
koinFragment = "org.koin:koin-androidx-fragment:2.1.5"
koinExt = "org.koin:koin-androidx-ext:2.1.5"
converterGson = "com.squareup.retrofit2:converter-gson:2.8.2"
logging = "com.squareup.okhttp3:logging-interceptor:3.14.9"
fastjson = "com.alibaba:fastjson:1.1.68.android"
coroutinesCore = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2"
coroutinesAndroid = "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"
urlManager = "me.jessyan:retrofit-url-manager:1.4.0"
baseAdapter = "com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4"
aviLib = "com.wang.avi:library:2.1.3"
finalLoadMore = "cn.finalteam.loadingviewfinal:loading-more-view:1.0.1"
finalRefresh = "cn.finalteam.loadingviewfinal:swipe-refresh-layout:1.0.1"
finalLoading = "cn.finalteam.loadingviewfinal:ultra-pull-to-refresh:1.0.1"
listAdapter = "com.zhy:base-adapter:3.0.3"
rvAdapter = "com.zhy:base-rvadapter:3.0.3"
systembartint = "com.readystatesoftware.systembartint:systembartint:1.0.3"
glance = "com.guolindev.glance:glance:1.1.0"
eventbus = "org.greenrobot:eventbus:3.1.1"
antifake = "com.snail:antifake:1.4"
glide = "com.github.bumptech.glide:glide:4.8.0"
lifecycleRuntime = "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"
lifecycleLivedata = "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
lifecycleViewmodel = "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
zxingLite = "com.king.zxing:zxing-lite:1.1.2"
backgroundLibrary = "com.github.JavaNoober.BackgroundLibrary:libraryx:1.7.3"
fragmentKtx = "androidx.fragment:fragment-ktx:1.4.1"
dsBridge = "com.github.wendux:DSBridge-Android:3.0-SNAPSHOT"
livedatabus = "io.github.jeremyliao:live-event-bus-x:1.8.0"
permissionx = "com.guolindev.permissionx:permissionx:1.6.4"
materialDialogs = "com.afollestad.material-dialogs:core:0.9.6.0"
navigationUi = "androidx.navigation:navigation-ui-ktx:2.3.5"
navigationFragment = "androidx.navigation:navigation-fragment-ktx:2.3.5"
}
这边是把各个模块的build.gradle都抽取出来了
接下来是自定义的3个gradle:
app.gradle:主工程
bss.gradle:业务模块,可切换library和application
lib.gradle:library模块
app.gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'org.jetbrains.kotlin.android'
def cfg = rootProject.ext
android {
compileSdk cfg.android.compileSdk
defaultConfig {
applicationId cfg.applicationId.app
minSdk cfg.android.minSdk
targetSdk cfg.android.targetSdk
versionCode cfg.android.versionCode
versionName cfg.android.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
//other
//仅业务模块需要用到AROUTER
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
ndk {
// 设置支持的SO库架构
abiFilters "armeabi-v7a"
}
}
buildTypes {
debug {
versionNameSuffix "-debug"
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
}
flavorDimensions 'app'
productFlavors {
zkt {
dimension 'app'
applicationId 'com.k.zkt'
}
}
}
dependencies {
kapt cfg.arouterCompiler
implementation project(path: ':base:libBase')
//不是调试模式的时候,trade和zkp才是library,才可以依赖
if (!cfg.isDebug) {
implementation project(path: ':bss:trade')
implementation project(path: ':bss:zkp')
}
}
bss.gradle
def cfg = rootProject.ext
if (cfg.isDebug) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'org.jetbrains.kotlin.android'
android {
compileSdk cfg.android.compileSdk
defaultConfig {
minSdk cfg.android.minSdk
targetSdk cfg.android.targetSdk
versionCode cfg.android.versionCode
versionName cfg.android.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
//other
//仅业务模块需要用到AROUTER
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
ndk {
// 设置支持的SO库架构
abiFilters "armeabi-v7a"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
//other
buildFeatures {
viewBinding true
}
//业务组件需要动态切换AndroidManifest
sourceSets {
main {
if (cfg.isDebug) {
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
}
lib.gradle
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'org.jetbrains.kotlin.android'
def cfg = rootProject.ext
android {
compileSdk cfg.android.compileSdk
defaultConfig {
//library是没有applicationId,versionCode,versionName
minSdk cfg.android.minSdk
targetSdk cfg.android.targetSdk
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
//other
buildFeatures {
viewBinding true
}
}
因为都抽取出来了,所以各模块下的build.gradle里面直接引用即可
上面介绍了有这几个模块:
include ':app'
include ':bss:trade'
include ':bss:zkp'
include ':func:pay'
include ':func:share'
include ':base:libBase'
include ':base:libRouter'
我们来看下各模块的配置:
app下的build.gradle配置:
apply from: app
def cfg = rootProject.ext
:bss:trade下的build.gradle配置:
apply from: bss
def cfg = rootProject.ext
android {
defaultConfig {
if (cfg.isDebug) {
applicationId cfg.applicationId.trade
}
}
}
dependencies {
kapt cfg.arouterCompiler
implementation project(path: ':base:libBase')
}
:bss:zkp下的build.gradle配置:
apply from: bss
def cfg = rootProject.ext
android {
defaultConfig {
if (cfg.isDebug) {
applicationId cfg.applicationId.zkp
}
}
}
dependencies {
kapt cfg.arouterCompiler
implementation project(path: ':base:libBase')
}
:func:pay下的build.gradle配置:
apply from: lib
def cfg = rootProject.ext
dependencies {
implementation project(':base:libBase')
}
:func:share下的build.gradle配置:
apply from: lib
def cfg = rootProject.ext
dependencies {
implementation project(':base:libBase')
}
:base:libBase下的build.gradle配置:
apply from: lib
def cfg = rootProject.ext
dependencies {
api cfg.library.corektx
api cfg.library.appcompat
api cfg.library.material
api cfg.library.constraintlayout
testImplementation cfg.library.junit
androidTestImplementation cfg.library.junitext
androidTestImplementation cfg.library.espressocore
api cfg.arouter
//这里可以依赖其他的一些工具库,主app和其他业务模块只依赖liBase即可
api project(path: ':base:libRouter')
api(name: 'test', ext: 'aar')
api cfg.litepal
api cfg.gson
api cfg.mmkv
api cfg.okhttp
api cfg.retrofit
api cfg.koinScope
api cfg.koinViewmodel
api cfg.koinFragment
api cfg.koinExt
api cfg.converterGson
api cfg.logging
api cfg.fastjson
api cfg.coroutinesCore
api cfg.coroutinesAndroid
api cfg.urlManager
api cfg.baseAdapter
api cfg.aviLib
api cfg.finalLoadMore
api cfg.finalRefresh
api cfg.finalLoading
api cfg.listAdapter
api cfg.rvAdapter
api cfg.systembartint
api cfg.eventbus
api cfg.antifake
api cfg.glide
api cfg.lifecycleRuntime
api cfg.lifecycleLivedata
api cfg.lifecycleViewmodel
api cfg.zxingLite
api cfg.backgroundLibrary
api cfg.fragmentKtx
api cfg.dsBridge
api cfg.livedatabus
api cfg.permissionx
api cfg.materialDialogs
api cfg.navigationUi
api cfg.navigationFragment
debugApi cfg.glance
}
:base:libRouter下的build.gradle配置:
apply from: lib
def cfg = rootProject.ext
dependencies {
api cfg.library.corektx
api cfg.library.appcompat
api cfg.library.material
api cfg.library.constraintlayout
testImplementation cfg.library.junit
androidTestImplementation cfg.library.junitext
androidTestImplementation cfg.library.espressocore
}
需要注意的是arouterCompiler每个业务组件都要分别依赖
业务组件需要动态切换AndroidManifest,由于每个业务组件都需要切换,所以就直接抽取到bss.gradle中去了。
根据bss.gradle中配置的路径配置相应的manifest
debug下的:
debug下的是可以独立运行的,所以会有android.intent.category.LAUNCHER
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.k.trade">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Zkt">
<activity
android:name=".TradeActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
默认的
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.k.trade">
<application>
<activity
android:name=".TradeActivity"
android:exported="false"
android:label=""
android:screenOrientation="portrait" />
</application>
</manifest>
其他业务组件同理,也是需要如上配置。
创建module的时候,如果需要加一个父级文件夹,name前多加一个即可
Gradle 简单语法见官方:Gradle 提示与诀窍