一、项目中的Gradle文件
新建了一个工程,Gradle文件如下
二、Gradle文件具体说明
1、gradle-wrapper.properties文件
如下:
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https://services.gradle.org/distributions/gradle-6.5-bin.zip
最重要的是最后一行,运行别人的项目时必须检查的地方。因为别人的Gradle版本本地不一定有,本都没有的话就会开始漫长的下载,所以运行别人的项目时先把这里改成跟本地一致。当然也可能别人用的Gradle版本跟这里配置的不一致,但这种概略比较小。
2、proguard-rules.pro文件
没有手动配置混淆规则时,里面都是注释。那一般配置文件有哪些内容:
#指定代码的压缩级别
-optimizationpasses 5
#包名不混合大小写
-dontusemixedcaseclassnames
#不忽略非公共的库类
-dontskipnonpubliclibraryclasses
#优化/不优化输入的类文件
-dontoptimize
#预校验
-dontpreverify
#混淆时是否记录日志
-verbose
#混淆时所采用的算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
#忽略警告
-ignorewarning
#保护注解
-keepattributes *Annotation*
#保护反射的正常调用
-keepattributes Signature
-keepattributes EnclosingMethod
#不混淆哪些类
-keep public class * extends android.app.Fragment
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
#不混淆所有View的子类及其子类的get、set方法
-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
public *** get*();
}
#指定不混淆所有的JNI方法
-keepclasseswithmembernames class * {
native <methods>;
}
#不混淆Activity中参数类型为View的所有方法
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
#不混淆Parcelable和它的子类,还有Creator成员变量
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#不混淆Serializable接口
-keepnames class * implements java.io.Serializable
#不混淆Serializable接口的子类中指定的某些成员变量和方法
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
!private <fields>;
!private <methods>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
#不混淆R类里及其所有内部static类中的所有static变量字段
-keepclassmembers class **.R$* {
public static <fields>;
}
#如果有用到Gson解析包的,直接添加下面这几行就能成功混淆,不然会报错。
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.examples.android.model.** { *; }
#如果有用到WebView的JS调用接口,需加入如下规则。
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
public *;
}
#apk包内所有class的内部结构
-dump class_files.txt
#未混淆的类和成员
-printseeds seeds.txt
#列出从apk中删除的代码
-printusage unused.txt
#混淆前后的映射
-printmapping mapping.txt
#忽略某个类的警告
-dontwarn com.unionpay.**
#不混淆某个类和成员变量
-keep class com.unionpay.** { *; }
-dontwarn android.support.**
-dontwarn com.flurry.**
-dontwarn com.paypal.**
-dontwarn org.lucasr.**
-dontwarn org.android.agoo.ut.impl.**
根据需求配置,有些类混淆时会出问题,比如webview的某些方法等。
3、gradle.properties(Project Properties)
全局的属性配置文件
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
//默认编码格式
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
//用androidx
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
//迁移指向android.support.* 软件包的Java、XML、POM 和ProGuard 引用,更改它们以使其指向相应的androidx.*
android.enableJetifier=true
//配置模块化的运行方式,singleModule=true表示每个模块都独立运行作为单独的apk,fasle标志作为module,被app模块依赖。
singleModule=false (后面会用到)
4、settings.gradle文件
模块的列表
如果是写一个Gradle plugins模块(插件)需要把插件的incluede删掉
include ':app' //模块
rootProject.name = "TestGradle" //根项目名称,改这里会改项目名称
5、local.properties文件
本地配置,比如sdk,ndk等
//配置项目支持NDK
ndk.dir=/Users/ailian/sdk/ndk-bundle
//配置项目支持SDK
sdk.dir=/Users/ailian/sdk
6、根目录的build.gradle文件
buildscript { //gradle脚本执行所需仓库和依赖
repositories { //仓库
google()
jcenter()
}
dependencies { //依赖
classpath 'com.android.tools.build:gradle:3.0.0' //gradle插件版本
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects { //项目需要的仓库
repositories {
google()
jcenter()
}
}
//task 运行会删掉build根文件夹
task clean(type: Delete) {
delete rootProject.buildDir
}
7、module的build.gradle文件
1)apply plugin
声明依赖的插件,其中application的插件可以动态配置是以apk还是以library运行,如下
//根据是否独立运行,将模块作为apk还是module
if (singleModule.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
2)添加签名配置
在android闭包中配置
signingConfigs {// 自动化打包配置
release {// 线上环境
keyAlias 'test'
keyPassword '123456'
storeFile file('test.keystore')
storePassword '123456'
}
debug {// 开发环境
keyAlias 'test'
keyPassword '123456'
storeFile file('test.keystore')
storePassword '123456'
}
}
在buildTypes闭包中使用
buildTypes {// 生产/测试环境配置
release {// 生产环境
buildConfigField("boolean", "LOG_DEBUG", "false")//配置Log日志
signingConfig signingConfigs.release//设置签名信息
...
}
debug {// 测试环境
buildConfigField("boolean", "LOG_DEBUG", "true")//配置Log日志
signingConfig signingConfigs.debug//设置签名信息
...
}
}
上面的代码还会生成一个配置参数,参数在Build.gradle文件中
通过BuildConfig.LOG_DEBUG可以直接取用。
3)配置目录指向
资源的配置,比如下面这种见过的写法
//资源配置
sourceSets {
main {
//jni
jni.srcDirs = []
jniLibs.srcDirs = ['libs']
//清单文件
manifest.srcFile 'src/main/AndroidManifest.xml'
//java文件
java.srcDirs = ['src/main/java', '.apt_generated']
//aidl
aidl.srcDirs = ['src/main/aidl', '.apt_generated']
//assets
assets.srcDirs = ['src/main/assets']
//资源文件(比如res外再添加res-main)
res.srcDirs += [
'src/main/res-main'
]
}
}
4)多渠道包
除了debug,release可以额外增加渠道包的flavors(风味),比如:
buildTypes {
debug{ //debug版本
}
release { //release版本
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
//额外增加二个风味,paying免费版还是付费版,nation中文版还是国际版
flavorDimensions 'paying','nation'
productFlavors{
free{ //免费版
dimension 'paying' //属于paying风味
applicationId 'com.abc.test.free'
}
paid{ //付费版
dimension 'paying' //属于paying风味
applicationId 'com.abc.test.paid'
}
chinese{ //中文版
dimension 'nation' //属于nation风味
applicationId 'com.abc.test.chinese'
}
international{ //国际版
dimension 'nation' //属于nation风味
applicationId 'com.abc.test.international'
}
}
那么一共有多少种包呢?
根据需要新建各种文件夹做版本粒度控制,打包时main文件夹内的内容加上粒度文件夹的内容会被打包进去。
把这些包打出来,先看下build.gradle的打包部分的代码
android {
signingConfigs {
release {
//多渠道打包的配置信息
storeFile file("../test.jks")
storePassword "123456"
keyAlias "test"
keyPassword "123456"
}
debug {
//多渠道打包的配置信息
storeFile file("../test.jks")
storePassword "123456"
keyAlias "test"
keyPassword "123456"
}
}
...
buildTypes {
debug{
signingConfig signingConfigs.debug //配置打包的参数
}
release {
signingConfig signingConfigs.release //配置打包的参数
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
flavorDimensions 'paying','nation'
productFlavors{
free{
dimension 'paying'
applicationId 'com.abc.test.free'
}
paid{
dimension 'paying'
applicationId 'com.abc.test.paid'
}
chinese{
dimension 'nation'
applicationId 'com.abc.test.chinese'
}
international{
dimension 'nation'
applicationId 'com.abc.test.international'
}
}
productFlavors.all {
//批量修改,类似一个循序遍历
flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
}
...
}
打包出来
渠道包指定格式输出的代码略。
5)完整build.gradle文件
这份完整文件来自一篇博客作为参考,文件最后有附链接。
// 声明是Android程序,
//com.android.application 表示这是一个应用程序模块
//com.android.library 标识这是一个库模块
//而这区别:前者可以直接运行,后着是依附别的应用程序运行
apply plugin: 'com.android.application'
android {
signingConfigs {// 自动化打包配置
release {// 线上环境
keyAlias 'test'
keyPassword '123456'
storeFile file('test.jks')
storePassword '123456'
}
debug {// 开发环境
keyAlias 'test'
keyPassword '123456'
storeFile file('test.jks')
storePassword '123456'
}
}
compileSdkVersion 27//设置编译时用的Android版本
defaultConfig {
applicationId "com.billy.myapplication"//项目的包名
minSdkVersion 16//项目最低兼容的版本
targetSdkVersion 27//项目的目标版本
versionCode 1//版本号
versionName "1.0"//版本名称
flavorDimensions "versionCode"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"//表明要使用AndroidJUnitRunner进行单元测试
}
buildTypes {// 生产/测试环境配置
release {// 生产环境
buildConfigField("boolean", "LOG_DEBUG", "false")//配置Log日志
buildConfigField("String", "URL_PERFIX", ""https://release.cn/"")// 配置URL前缀
minifyEnabled false//是否对代码进行混淆
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'//指定混淆的规则文件
signingConfig signingConfigs.release//设置签名信息
pseudoLocalesEnabled false//是否在APK中生成伪语言环境,帮助国际化的东西,一般使用的不多
zipAlignEnabled true//是否对APK包执行ZIP对齐优化,减小zip体积,增加运行效率
applicationIdSuffix 'test'//在applicationId 中添加了一个后缀,一般使用的不多
versionNameSuffix 'test'//在applicationId 中添加了一个后缀,一般使用的不多
}
debug {// 测试环境
buildConfigField("boolean", "LOG_DEBUG", "true")//配置Log日志
buildConfigField("String", "URL_PERFIX", ""https://test.com/"")// 配置URL前缀
minifyEnabled false//是否对代码进行混淆
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'//指定混淆的规则文件
signingConfig signingConfigs.debug//设置签名信息
debuggable false//是否支持断点调试
jniDebuggable false//是否可以调试NDK代码
renderscriptDebuggable false//是否开启渲染脚本就是一些c写的渲染方法
zipAlignEnabled true//是否对APK包执行ZIP对齐优化,减小zip体积,增加运行效率
pseudoLocalesEnabled false//是否在APK中生成伪语言环境,帮助国际化的东西,一般使用的不多
applicationIdSuffix 'test'//在applicationId 中添加了一个后缀,一般使用的不多
versionNameSuffix 'test'//在applicationId 中添加了一个后缀,一般使用的不多
}
}
sourceSets {//目录指向配置
main {
jniLibs.srcDirs = ['libs']//指定lib库目录
}
}
packagingOptions{//打包时的相关配置
//pickFirsts做用是 当有重复文件时 打包会报错 这样配置会使用第一个匹配的文件打包进入apk
// 表示当apk中有重复的META-INF目录下有重复的LICENSE文件时 只用第一个 这样打包就不会报错
pickFirsts = ['META-INF/LICENSE']
//merges何必 当出现重复文件时 合并重复的文件 然后打包入apk
//这个是有默认值得 merges = [] 这样会把默默认值去掉 所以我们用下面这种方式 在默认值后添加
merge 'META-INF/LICENSE'
//这个是在同时使用butterknife、dagger2做的一个处理。同理,遇到类似的问题,只要根据gradle的提示,做类似处理即可。
exclude 'META-INF/services/javax.annotation.processing.Processor'
}
productFlavors {
wandoujia {}
xiaomi {}
_360 {}
}
productFlavors.all {
//批量修改,类似一个循序遍历
flavor -> flavor.manifestPlaceholders = [IFLYTEK_CHANNEL: name]
}
//程序在编译的时候会检查lint,有任何错误提示会停止build,我们可以关闭这个开关
lintOptions {
abortOnError false
//即使报错也不会停止打包
checkReleaseBuilds false
//打包release版本的时候进行检测
}
}
dependencies {
//项目的依赖关系
implementation fileTree(include: ['*.jar'], dir: 'libs')
//本地jar包依赖
implementation 'com.android.support:appcompat-v7:27.1.1'
//远程依赖
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
testImplementation 'junit:junit:4.12'
//声明测试用例库
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
三、Gradle文件的统一管理
当模块变多,多份build.gradle难免会出现各种问题,比如依赖冲突,版本不一致等。那如何做统一管理,下面是二种方式,写法不固定重要的是思路。孰优孰劣,仁者见仁。
1、方式一
将build.gradle需要的配置做统一管理,在根目录创建gradle文件
这个文件的配置大致如下
//配置变量给下面的依赖用
def appcompat_version = "1.1.0"
def recyclerview = "1.1.0"
def rxjava = "2.2.8"
def rxandroid = "2.1.1"
def retrofitVersion = "2.8.1"
project.ext {
android = [ //配置android的一些参数
compileSdkVersion: 30,
buildToolsVersion: "30.0.1",
applicationId : "com.abc.abcde",
minSdkVersion : 19,
targetSdkVersion : 30,
versionCode : 1,
versionName : "1.0"
]
dependencies = [ //配置依赖的一些参数
//android-support
"appcompat" : "androidx.appcompat:appcompat:${appcompat_version}",
"recyclerview" : "androidx.recyclerview:recyclerview:${recyclerview}",
//rxjava
"rxjava" : "io.reactivex.rxjava2:rxjava:${rxjava}",
"rxandroid" : "io.reactivex.rxjava2:rxandroid:${rxandroid}",
// Retrofit
"retrofit" : "com.squareup.retrofit2:retrofit:${retrofitVersion}",
"adapter-rxjava" : "com.squareup.retrofit2:adapter-rxjava2:${retrofitVersion}",
"converter-gson" : "com.squareup.retrofit2:converter-gson:${retrofitVersion}",
"logging-interceptor" : "com.squareup.okhttp3:logging-interceptor:${okhttpLogInterceptorVersion}",
]
}
在根目录的build.gradle加上引用这个gradle文件的代码
apply from: "dependencies.gradle"
在module的build.gradle文件中使用
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
}
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
dataBinding {
enabled = true
}
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
//引用依赖
implementation rootProject.ext.dependencies["appcompat"]
implementation rootProject.ext.dependencies["rxjava"]
2、方式二
所有的module都引用同一份gradle文件,这一份文件包含了build.gradle需要的所有配置,所有module的依赖项也是一样的。每个module的build.gradle维护自己特有的属性,其他的都由同一份gradle文件来管理。
新建一个gradle文件
这个文件的配置如下
//配置各个module共用的插件
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'//kotlin语言使用注入框架,dataBinding都需要这个
apply plugin: 'androidx.navigation.safeargs.kotlin'//用于navigation传参的时候一种安全防护,非空,类型判断等。
android {
//还是做一些基础的抽取管理
compileSdkVersion compile_sdk_version
buildToolsVersion build_tools_version
defaultConfig {
minSdkVersion min_sdk_verion
targetSdkVersion target_sdk_version
versionCode lib_version_code
versionName lib_version_name
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
//AS4 以上版本使用这个配置dataBinding
buildFeatures {
dataBinding = true
}
compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
//使用jetpack的组件,会要求jdk 1.8最好,有的时候会遇到报错,需要配置如下,避免target jvm 1.8的那个报错
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
//region 平台相关的基础库
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation("androidx.core:core-ktx:1.3.1")
implementation 'androidx.fragment:fragment-ktx:1.2.4'
implementation 'androidx.activity:activity-ktx:1.1.0'
implementation "androidx.constraintlayout:constraintlayout:2.0.0-beta8"
//endregion
//<edit-folder desc="优秀第三方必备框架库">
// Koin for Kotlin
implementation "org.koin:koin-core:$koin_version"
implementation "org.koin:koin-core-ext:$koin_version"
// Koin for Androidx
implementation "org.koin:koin-androidx-scope:$koin_version"
implementation "org.koin:koin-androidx-viewmodel:$koin_version"
implementation "org.koin:koin-androidx-fragment:$koin_version"
implementation "org.koin:koin-androidx-ext:$koin_version"
testImplementation "org.koin:koin-test:$koin_version"
//</edit-folder>
//region jetPack libs
implementation 'androidx.lifecycle:lifecycle-livedata:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'
implementation 'androidx.lifecycle:lifecycle-runtime:2.2.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
kapt "androidx.lifecycle:lifecycle-common-java8:2.2.0"
implementation 'androidx.room:room-runtime:2.2.5'
implementation 'androidx.room:room-common:2.2.5'
implementation 'androidx.room:room-ktx:2.2.5'
kapt "androidx.room:room-compiler:2.2.5"
implementation 'androidx.paging:paging-runtime:2.1.2'
implementation 'androidx.paging:paging-runtime-ktx:2.1.2'
implementation 'androidx.paging:paging-common:2.1.2'
implementation 'androidx.paging:paging-common-ktx:2.1.2'
implementation 'androidx.work:work-runtime:2.4.0'
implementation 'androidx.work:work-runtime-ktx:2.4.0'
//注意配置navigation版本号的时候,project下的build.gradle的class path也需要同步navigation的版本号配置
implementation "androidx.navigation:navigation-ui:$navigation_version"
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
implementation "androidx.navigation:navigation-fragment:$navigation_version"
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
implementation "androidx.navigation:navigation-runtime:$navigation_version"
//endregion
...
}
抽取的android的版本管理因为不多就直接放在根目录的build.gradle文件中
ext {
kotlin_version = '1.4.10'
compile_sdk_version = 29
build_tools_version = '29.0.3'
min_sdk_verion = 21
target_sdk_version = 29
lib_version_code = 1
lib_version_name = "1.0.0"
navigation_version = "2.2.1"//navigation 版本号,需要在dependencies和classpath保持一致
koin_version = '2.1.6'//依赖注入框架
}
所有的module在build.gradle文件中引用这个文件,并配置自己特有的属性即可
//引用dependencies.gradle
apply from: '../dependencies.gradle'
//根据是否独立运行,将模块作为apk还是module
if (singleModule.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
android {
//from dependencies.gradle
defaultConfig{
//只有独立运行时候才需要applicationId
if (singleModule.toBoolean()) {
applicationId "com.abc.app.login"
}
}
}
dependencies {
//依赖common模块
api project(path: ':common')
}