Android组件化架构 - 9.Gradle优化
2020-12-30 本文已影响0人
今阳说
gradle本质是一个自动化构建工具, 使用基于groovy的特定领域语言来声明项目设置;
- 根目录的gradle文件
buildscript {//构建脚本引用
ext {
kotlin_version = '1.3.72'
}
repositories {//插件仓库配置
google()
jcenter()
mavenCentral()
}
dependencies {//依赖插件
//google的Android Gradle插件
classpath 'com.android.tools.build:gradle:3.5.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
//美团walle多渠道打包插件
classpath 'com.meituan.android.walle:plugin:1.1.6'
//kotlin插件
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
//greendao插件
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'
//ARouter插件
classpath "com.alibaba:arouter-register:1.0.2"
}
}
allprojects {//全部项目的配置
repositories {//全部项目引用的基础仓库配置
google()
jcenter()
mavenCentral()
maven { url 'http://developer.seedland.cc/nexus/repository/maven-dev/' }
maven { url 'https://jitpack.io' }
}
}
task clean(type: Delete) {//声明任务
delete rootProject.buildDir//删除主路径buildDir文件夹
}
- app module的gradle文件:
//引入构建需要用到的gradle插件工具库
//每个build.gradle自身是一个Project对象,project.apply()会加载某个工具库到project对象中
//apply plugin:'xxx' 会将project对象传递入工具库,然后通过插件中的Groovy文件来操作
//project对象的属性, 以完善配置初始化信息
apply plugin: 'com.android.application'
apply plugin: 'walle'
apply plugin: 'kotlin-android'
apply plugin: 'org.greenrobot.greendao'
apply plugin: 'kotlin-kapt'
apply plugin: 'com.alibaba.arouter'
//android{} ,dependencies{} 是函数方程式,使用闭包函数的编写方式,
//相当于project.android(){} ,project.dependencies(){}
android {
compileSdkVersion 30 //编译的sdk
defaultConfig { //默认配置
applicationId "com.ljy.publicdemo" //包名
minSdkVersion 17 //最低支持版本
targetSdkVersion 30 //支持的目标版本
versionCode 1 //版本号
versionName "1.0" //版本名
multiDexEnabled true //dex分包支持
//如果应用没有做国际化,可以让应用仅支持 中文的资源配置
resConfigs "zh"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" //测试脚本
ndk {
//选择要添加的对应cpu类型的.so库。
abiFilters 'armeabi', 'armeabi-v7a', 'x86', 'arm64-v8a', 'x86_64'
// 还可以添加 'x86', 'x86_64', 'mips', 'mips64'
}
//ARouter配置
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
}
}
}
signingConfigs {//签名配置
key {
keyAlias 'key0'
keyPassword '666666'
storePassword '666666'
storeFile file('./ljy_key.jks')
}
}
lintOptions {
// true--关闭lint报告的分析进度
quiet true
// true--错误发生后停止gradle构建
abortOnError false
// true--只报告error
ignoreWarnings true
// true--忽略有错误的文件的全/绝对路径(默认是true)
//absolutePaths true
// true--检查所有问题点,包含其他默认关闭项
checkAllWarnings true
// true--所有warning当做error
warningsAsErrors true
// 关闭指定问题检查
disable 'TypographyFractions','TypographyQuotes'
// 打开指定问题检查
enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
// 仅检查指定问题
check 'NewApi', 'InlinedApi'
// true--error输出文件不包含源码行号
noLines true
// true--显示错误的所有发生位置,不截取
showAll true
// 回退lint设置(默认规则)
lintConfig file("default-lint.xml")
// true--生成txt格式报告(默认false)
textReport true
// 重定向输出;可以是文件或'stdout'
textOutput 'stdout'
// true--生成XML格式报告
xmlReport false
// 指定xml报告文档(默认lint-results.xml)
xmlOutput file("lint-report.xml")
// true--生成HTML报告(带问题解释,源码位置,等)
htmlReport true
// html报告可选路径(构建器默认是lint-results.html )
htmlOutput file("lint-report.html")
// true--所有正式版构建执行规则生成崩溃的lint检查,如果有崩溃问题将停止构建
checkReleaseBuilds true
// 在发布版本编译时检查(即使不包含**重点内容**lint目标),指定问题的规则生成崩溃
fatal 'NewApi', 'InlineApi'
// 指定问题的规则生成错误
error 'Wakelock', 'TextViewEdits'
// 指定问题的规则生成警告
warning 'ResourceAsColor'
// 忽略指定问题的规则(同关闭检查)
ignore 'TypographyQuotes'
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
flavorDimensions "jin"
productFlavors {
higher {
applicationId "com.ljy.publicdemo"
minSdkVersion 26
buildConfigField 'boolean', 'isLite', "false"
// resValue "string", "app_name_new", "JinDemo"
manifestPlaceholders = [
app_icon : "@mipmap/ic_launcher_normal",
channelName : "higher",
verNum : "2",
app_name_new: "JinDemoHigher"
]
}
lower {
applicationId "com.ljy.publicdemo.lite"
minSdkVersion 17
buildConfigField 'boolean', 'isLite', "true"
// resValue "string", "app_name_new", "JinDemoLite"
manifestPlaceholders = [
app_icon : "@mipmap/ic_launcher_lite",
channelName : "lower",
verNum : "1",
app_name_new: "JinDemoLower"
]
}
}
buildTypes {
release {
minifyEnabled false
buildConfigField("boolean", "isDebug", "false")
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.key
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "LjyPublicDemo_${variant.buildType.name}_${variant.versionName}.apk"
}
}
}
debug {
minifyEnabled false
buildConfigField("boolean", "isDebug", "true")
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.key
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "LjyPublicDemo_${variant.buildType.name}_${variant.versionName}.apk"
}
}
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
// 配置数据库相关信息
greendao {
//数据库版本号
schemaVersion 1
// 设置DaoMaster、DaoSession、Dao 包名
daoPackage 'com.ljy.publicdemo.greendao'
//设置DaoMaster、DaoSession、Dao目录
targetGenDir 'src/main/java'
}
//gradle的命名提示机制,让各module中资源名必须按一定前缀命名,有助于防止组件化多module时的资源冲突
resourcePrefix 'app_'
}
walle {
// 指定渠道包的输出路径
apkOutputFolder = new File("${project.buildDir}/outputs/channels");
// 定制渠道包的APK的文件名称
apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk';
// 渠道配置文件
channelFile = new File("${project.getProjectDir()}/channel")
}
tasks.withType(JavaCompile) {
//在 Gradle 4.10 版本之后便默认使用了增量编译
//如果在更老的版本需要启动增量编译,可以使用如下配置:
options.incremental = true
//解决java控制台输出中文乱码
options.encoding = "UTF-8"
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation project(path: ':mylibrary')
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation project(':library')
}
gradle的生命周期分三个阶段:
1. 初始化: settings.gradle中声明module, 决定哪些工程会加入构建过程,并且创建project对象
include ':app', ':library',':datepicker',':heigher_setting',':lower_setting',':mylibrary'
2. 配置: 按引用树去执行所有工程的build.gradle脚本,配置project对象,一个project对象由多个任务组成
3. 构建: 运行阶段会根据gradle命令传递过来的task名称,执行相关依赖任务
- 版本参数优化
每个module的build.gradle文件都拥有一些必要的属性,同一个Android工程中,在不同的模块中要求一些属性一致,
如compileSdkVersion,如果引用不一致,属性不会被合并引入到工程中,这样一方面会造成资源的重复,包体积增大,另一方面会降低编译效率;
那么就需要有一个统一的,基础的gradle配置
- 优化方案 1: 使用共同参数的方式进行配置
//创建一个 common_config.gradle文件
project.ext {
compileSdkVersion = 30
buildToolsVersion = "30.0.0"
minSdkVersion = 17
targetSdkVersion = 30
applicationId = "com.ljy.publicdemo"
versionCode = 1
versionName ="1.0"
}
//在module的gradle首行引用common_config,并通过类似静态变量的方式引用属性
apply from: "${rootProject.rootDir}/common_config.gradle"//引用额外配置
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion project.ext.compileSdkVersion
buildToolsVersion project.ext.buildToolsVersion
defaultConfig {
minSdkVersion project.ext.minSdkVersion
targetSdkVersion project.ext.targetSdkVersion
versionCode project.ext.versionCode
versionName project.ext.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
- 优化方案 2: 使用Android对象配置
//android{}中提供了android这个变量,那么可以进一步简化代码,在common_config.gradle的
//project.ext中添加一个闭包方法来指定project对象的变量
project.ext {
compileSdkVersion = 30
buildToolsVersion = "30.0.0"
applicationId = "com.ljy.publicdemo"
minSdkVersion = 17
targetSdkVersion = 30
versionCode = 1
versionName = "1.0"
setDefaultConfig = {
extension -> //闭包参数extension相当于android对象
extension.compileSdkVersion compileSdkVersion
extension.buildToolsVersion buildToolsVersion
extension.defaultConfig {
applicationId applicationId
minSdkVersion minSdkVersion
targetSdkVersion targetSdkVersion
versionCode versionCode
versionName versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
extension.dataBinding {
enabled true
}
}
}
//在bulid.gradle中就可以使用 setDefaultConfig了,类似属性设置,实际上是函数调用
apply from: "${rootProject.rootDir}/common_config.gradle"//引用额外配置
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
//使用闭包函数输入android这个对象到函数中
project.ext.setDefaultConfig android
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
- 优化方案 3: 使用project对象配置(最终版本)
//common_config.gradle:
project.ext {
compileSdkVersion = 30
buildToolsVersion = "30.0.0"
applicationId = "com.ljy.publicdemo"
minSdkVersion = 17
targetSdkVersion = 30
versionCode = 1
versionName = "1.0"
//设置app module 配置
setAppDefaultConfig = {
extension ->
//引用application插件库
extension.apply plugin: 'com.android.application'
extension.description "app"
setAndroidConfig extension.android
setDependencies extension.dependencies
}
//设置lib module 配置
setLibDefaultConfig = {
extension ->
//引用Lib插件库
extension.apply plugin: 'com.android.library'
extension.description "lib"
setAndroidConfig extension.android
setDependencies extension.dependencies
}
//设置android配置
setAndroidConfig = {
extension -> //闭包参数extension相当于android对象
extension.compileSdkVersion compileSdkVersion
extension.buildToolsVersion buildToolsVersion
extension.defaultConfig {
minSdkVersion minSdkVersion
targetSdkVersion targetSdkVersion
versionCode versionCode
versionName versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
//ARouter配置
javaCompileOptions {
annotationProcessorOptions {//路由每个模块的名称
arguments = [AROUTER_MODULE_NAME: extension.project.getName()]
}
}
}
extension.dataBinding {
enabled true
}
}
//设置依赖
setDependencies={
extension ->
extension.implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
//每个module都需要应用路由的apt插件库才能生成相应代码,这里无需重复编写每个module的gradle
extension.annotationProcessor 'com.alibaba:arouter-compiler:1.2.1'
}
}
//build.gralde中只需要传入project对象到闭包函数中即可
- 调试优化
业务模块调试, 将单一模块作为app启动, 然后用于调试测试, 这样保证了单独模块可以分离调试;
1. 业务模块是library module, 需要将apply plugin: 'com.android.library'
变为 apply plugin: 'com.android.application',
将模块作为application module才能引入app构建流程;
2. 每个application都需要配置applicationId
3. androidMainfest中需要配置默认启动的Activity
<activity android:name=".datepicker.DatePickerActivity"
android:theme="@style/Theme.AppCompat">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
4. common_config中声明控制模块是否调试的变量
project.ext {
compileSdkVersion = 30
buildToolsVersion = "30.0.0"
minSdkVersion = 17
targetSdkVersion = 30
versionCode = 1
versionName = "1.0"
//控制组件模块调试
isDatePickerDebug=true
}
5. 在模块的build.config中将变量作为开关
apply from: "${rootProject.rootDir}/common_config.gradle"
if (project.isDatePickerDebug) {
project.ext.setAppDefaultConfig project//设置app配置
} else {
project.ext.setLibDefaultConfig project//设置lib配置
}
android{
sourceSets {
main {
if (project.isDatePickerDebug) {
manifest.srcFile 'src/debug/AndroidManifest.xml'
res.srcDirs = ['src/debug/res', 'src/main/res']
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
resources {
exclude 'src/debug/*'
}
}
}
}
}
6. 原 app module 下,需要移除已经单独调试的模块的依赖
if (!project.isDatePickerDebug) {
implementation project(':datepicker')
}
- 资源引用配置
gradle有多种资源引用的方式
(1) 使用sourceSets指定文件路径
sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml' //AndroidManifest路径
jniLibs.srcDirs = ['libs']//so库路径
resources.srcDirs = ['src']//全部资源文件路径
aidl.srcDirs = ['src']//aidl文件路径
renderscript.srcDirs = ['src']//renderscript文件路径
res.srcDirs = ['res']//res资源文件路径
assets.srcDirs = ['assets']//asset资源文件路径
}
}
(2)可以动态添加res资源,在 buildTypes,productFlavors 中定义 resValue 变量,
resValue只能动态添加资源,不能替换,如果资源名重复,gradle会提示重复资源
resValue "string", "app_name_new", "JinDemo"
(3) 可以指定特定尺寸的资源,在 buildTypes, productFlavors 中定义 resConfigs
android {
...
defaultConfig {
...
resConfigs "zh","en"//剔除不需要的国际化
resConfigs "hdpi","xhdpi","xxhdpi","xxxhdpi"//剔除自身,三方库,sdk中不需要的dpi资源
ndk {
//选择要添加的对应cpu类型的.so库。
abiFilters 'armeabi', 'armeabi-v7a' //, 'x86', 'arm64-v8a', 'x86_64', 'mips', 'mips64'
}
}
}
(4) productFlavors中,通过buildConfigField可以让代码直接读取到BuildConfig中的值,通过manifestPlaceholders配合AndroidManifest中的meta-data可以间接让代码读取到AndroidManifest的变量
productFlavors {
higher {
buildConfigField 'boolean', 'isLite', "false"
manifestPlaceholders = [
app_icon : "@mipmap/ic_launcher_normal",
channelName : "higher",
verNum : "2",
app_name_new: "JinDemoHigher"
]
}
}
Gradle加载优先级
在build.gradle中,有四个函数,优先级由高到低为:
buildTypes > productFlavors > Main > dependencies