1 什么是Android组件化?2 Android组件化部署 --- Gradle的用法2.1 Gradle控制测试环境和线上环境问题1:整个app的根是什么?问题2 : 抽取gradle中公共的代码问题3:Gradle配置线上和测试环境问题4:Gradle配置模块是否能够独立运行问题5:如果是线上环境打包,不想打进去测试代码
1 什么是Android组件化?Android组件化是工程化的思想之一,如果在项目中不区分模块,所有的模块都放在一起,耦合度非常高,每个模块之前无法分离,不具备插拔特性,而且不易于扩展维护
那么组件化的优势在哪?
首先组件化能够使得整个项目的架构更加清晰,各个模块之间是分离的,而且每个模块能够独立运行,每个组件之前可以自由组合,拆分
通过下面这个图,来看一下组件化是什么样的
app作为一个壳,包裹下面所有的业务层组件和基础层组件,而且组件1 组件2 … 他们之间是不能平行通信的,需要通过基础层路由,来完成组件之前的通信,这样就实现了组件之前的相互隔离
了解什么是组件化之后,这里就会介绍,怎样才能完成组件化的部署
这里说一些常见的场景:
1 通常在一个产品上线之前,需要在测试环境中开发,测试,等到功能完善测试完成之后,完成封板,转为线上环境,这就需要在打包时,来区分测试环境还是线上环境
2 在打包时,我不需要将所有的模块都打包,我只需要测试其中1个模块,那么这就需要每个模块之间都能够独立运行
3 打包发布时,需要把所有的模块都打包,这就是集成化的概念,要求app壳能够独立运行,而且每个模块都需要依附于app壳
2.1 Gradle控制测试环境和线上环境问题1:整个app的根是什么?
整个app的根就是 setting.gradle
根目录 # settings.gradle
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
jcenter() // Warning: this repository is going to shut down soon
}
}
//项目的名称 modulelization
//包含app壳,以及2个模块 login register
//还有2个Android library
rootProject.name = "modulelization"
include ':app'
include ':moudle1'
include ':moudle2'
include ':business'
include ':login'
include ':register'
执行的顺序:从setting.gradle开始,找到根目录的build.gradle,然后再找到app壳的build.gradle
在每个模块的gradle中,输出一句话,build的顺序就是按照在工程中的顺序依次打印出来的
> Configure project :app 组件化 > Configure project :login 组件化 login > Configure project :moudle1 组件化 moudle 01 > Configure project :moudle2 组件化 moudle 02 > Configure project :register 组件化 register
问题2 : 抽取gradle中公共的代码
在一个项目中,每个模块都存在一个gradle,而且每个gradle中都存在很多相似的代码,尤其是一些版本号,如果需要修改版本号,那么每个gradle都要修改一遍吗?
所以这里就需要把每个模块的相同代码抽取出来,在app的根目录下创建一个gradle(config.gradle),添加一个扩展块ext
ext{
//
compileSdk 31
defaultConfig {
applicationId "com.study.login"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
在gradle中,相似的代码见上文,都是一些key value的形式,那么可以做一个map给放进去
根目录 # config.gradle
//扩展块
ext{
//建立map存储
androidId = [
compileSdk : 31,
applicationId : "com.study.login",
minSdk : 21,
targetSdk : 31,
versionCode : 1,
versionName : "1.0",
testInstrumentationRunner : "android.support.test.runner.AndroidJUnitRunner",
]
}
如果想要使用这个扩展,需要放在项目根gradle中引入,才可以使用
根目录 # build.gradle
//引入创建的 config.gradle
apply from : 'config.gradle'
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:7.0.3"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
这样在每个模块中,都可以使用自定义的map中的值,而且修改时,只需要修改config.gradle中的字段就可以
//优化 提高性能
def config = rootProject.ext.androidId
android {
compileSdk config.compileSdk
defaultConfig {
applicationId "com.study.modulelization"
minSdk config.minSdk
targetSdk config.targetSdk
versionCode config.versionCode
versionName config.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
除此之外,还有gradle中配置的依赖,如果有300行的依赖需要配置,那么在修改每个库的版本号时,需要逐一修改,这里也可以通过公共抽取配置
app # build.gradle
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
config.gradle
dependency = [
coreKtx : 'androidx.core:core-ktx:1.7.0',
appcompat : 'androidx.appcompat:appcompat:1.4.1',
material : 'com.google.android.material:material:1.5.0',
constraintlayout : 'androidx.constraintlayout:constraintlayout:2.1.3'
]
修改后的 app # build.gradle
dependencies {
implementation dependency.coreKtx
implementation dependency.appcompat
implementation dependency.material
implementation dependency.constraintlayout
//这种视情况而定
dependency.each{k,v -> implementation v}
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
这样设置也没有问题,既然是一个map,那就可以一行代码实现全部的依赖
问题3:Gradle配置线上和测试环境
传统的老办法,在区分线上或者测试环境,都是通过配置不同的URL,通过代码设置线上或者测试环境
class HttpConfig {
companion object{
private const val DEBUG = "http://106.108.2.3//test-baidu.com"
private const val RELEASE = "http://106.108.2.3//baidu.com"
}
}
那么在Gradle中,如何配置线上或者测试环境的切换
config.gradle # ext
isRelease = false
//线上 测试环境配置
url = [
DEBUG : "http://106.108.2.3//test-baidu.com",
RELEASE: "http://106.108.2.3//baidu.com"
]
app壳 # build.gradle
defaultConfig {
applicationId "com.study.modulelization"
minSdk config.minSdk
targetSdk config.targetSdk
versionCode config.versionCode
versionName config.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
//三个参数
//字段类型 字段名 字段值
buildConfigField("Boolean","isRelease","${rootProject.isRelease}")
}
buildTypes {
debug{
buildConfigField("String","debugUrl",""${rootProject.url.DEBUG} "")
}
release {
buildConfigField("String","releaseUrl",""${rootProject.url.RELEASE}"")
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
如果要在Java中引入Gradle中配置的数据,就需要使用到buildConfigField,其中需要传入三个参数,见注释即可
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "com.study.modulelization";
public static final String BUILD_TYPE = "debug";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0";
// Field from build type: debug
public static final String debugUrl = "http://106.108.2.3//test-baidu.com ";
// Field from default config.
public static final Boolean isRelease = false;
}
这样在BuildConfig中,就看到了这几个字段,这种就可以把之前用代码写的配置代码删除掉,因为太low了
HttpConfig
companion object{
val hostUrl:String = if(BuildConfig.isRelease) BuildConfig.debugUrl else BuildConfig.releaseUrl
}
这样在HttpConfig文件中,加这一行即可
问题4:Gradle配置模块是否能够独立运行
在模块的gradle中,存在两个不同的plugins,分别是”com.android.library“ 和 ”com.android.application“,其中library是不能独立运行的,而application是可以独立运行的
Library projects cannot set applicationId. applicationId is set to ‘com.study.register’ in default config.
还有一个区分就是library是不能有applicationId的,而application能够独立运行的是有applicationId的
register # build.gradle
if(isRelease){
apply plugin : 'com.android.library'
}else{
apply plugin : 'com.android.application'
}
apply plugin: 'kotlin-android'
println("组件化 register")
android {
compileSdk 31
defaultConfig {
if(!isRelease){
applicationId "com.study.register"
}
其中不可独立运行,app壳是能够依赖的,但是如果能够独立运行,app壳是不能依赖的
app壳 # build.gradle
if(rootProject.isRelease){
//依赖包
implementation project(path: ':register')
}
问题5:一个模块单独运行,如何区分测试环境和线上环境
app启动展示的界面是通过AndroidManifest来保存界面数据,可以设置首页展示的页面,但是为了能够区分测试环境和线上环境,不能通过下面这种方式来判断
那么通过gradle来判断测试或者线上环境,来分配不同的清单文件AndroidManifest就可以完成线上或者测试的区分
register # build.gradle
sourceSets{
main{
if(rootProject.isRelease){
manifest.srcFile 'src/main/AndroidManifest.xml'
}else{
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
}
}
}
在register中创建两个Manifest文件区分一下,通过sourceSets用来设置文件的存放位置,如果是测试环境,就从测试debug文件夹下拿AndroidManifest文件
问题5:如果是线上环境打包,不想打进去测试代码
当上线打包后,因为测试代码会增大包体积,那么在线上环境的时候,需要把debug目录下的代码全部屏蔽掉
sourceSets{
main{
if(rootProject.isRelease){
manifest.srcFile 'src/main/AndroidManifest.xml'
//屏蔽测试代码
java {
exclude("**/debug/**")
}
}else{
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
}
}
}



