Android工程Gradle构建-笔记
1、统一版本库管理
1.1、统一版本号管理
创建一个gradle文件统一管理 不同module下的第三方库和其他属性的配置参数 如下,在项目根目录创建config.gradle
ext {
COMPILE_SDK = 30
APPLICATION_ID = "com.chenyangqi.gradle"
MIN_SDK = 19
TARGET_SDK = 30
VERSION_CODE = 1
VERSION_NAME = "1.0.0"
JVM_TARGET = '1.8'
MULTIDEX = 'androidx.multidex:multidex:2.0.1'
CORE_KTX = 'androidx.core:core-ktx:1.3.2'
APPCOMPAT = 'androidx.appcompat:appcompat:1.2.0'
ANDROID_MATERIAL = 'com.google.android.material:material:1.3.0'
CONSTRAINTLAYOUT = 'androidx.constraintlayout:constraintlayout:2.1.0'
TEST_JUNIT = 'junit:junit:4.+'
ANDROID_EXT_JUNIT = 'androidx.test.ext:junit:1.1.2'
ANDROID_TEST_ESPRESSO = 'androidx.test.espresso:espresso-core:3.3.0'
}
然后在项目的gradle中引用config.gradle
apply from: project.file('config.gradle')
1.2、local.perporties使用场景
一些不便对外展示的敏感参数可以定义在local.properties中,如一些付费库的key,maven仓库的用户名和密码等
sdk.dir=/Users/mutou/Library/Android/sdk
username=chenyangqi
password=123456
编译时通过如下方式获取local.properties中定义的属性
Properties properties = new Properties()
properties.load(project.rootProject.file("local.properties").newDataInputStream())
def username = properties.getProperty('username')
def password = properties.getProperty('password')
在运行时获得Local.properties属性要借助BuildConfig
def getUsername() {
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
return properties.getProperty("username");
}
android {
defaultConfig {
buildConfigField "String", "USERNAME", "\""+getUsername()+"\""
}
}
1.3、版本冲突处理
出现依赖冲突时可通过gradle的dependencies查看依赖树,定位冲突位置 ,比如我要查看的Build Variants为oppoNormalProdRelease,命令如下
./gradlew :app:dependencies --configuration oppoNormalProdReleaseCompileClasspath
依赖树如下
mutou@chenyangqi GradleDemo % ./gradlew :app:dependencies --configuration oppoNormalProdReleaseCompileClasspath
...
oppoNormalProdReleaseCompileClasspath - Compile classpath for compilation 'oppoNormalProdRelease' (target (androidJvm)).
+--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.21
| +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.21
| | +--- org.jetbrains:annotations:13.0
| | \--- org.jetbrains.kotlin:kotlin-stdlib-common:1.5.21
| \--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.21
| \--- org.jetbrains.kotlin:kotlin-stdlib:1.5.21 (*)
+--- androidx.multidex:multidex:2.0.1
+--- androidx.core:core-ktx:1.3.2
| +--- org.jetbrains.kotlin:kotlin-stdlib:1.3.71 -> 1.5.21 (*)
| +--- androidx.annotation:annotation:1.1.0
| \--- androidx.core:core:1.3.2
| +--- androidx.annotation:annotation:1.1.0
| +--- androidx.lifecycle:lifecycle-runtime:2.0.0 -> 2.1.0
| | +--- androidx.lifecycle:lifecycle-common:2.1.0
| | | \--- androidx.annotation:annotation:1.1.0
| | +--- androidx.arch.core:core-common:2.1.0
| | | \--- androidx.annotation:annotation:1.1.0
| | \--- androidx.annotation:annotation:1.1.0
| \--- androidx.versionedparcelable:versionedparcelable:1.1.0
| +--- androidx.annotation:annotation:1.1.0
| \--- androidx.collection:collection:1.0.0 -> 1.1.0
| \--- androidx.annotation:annotation:1.1.0
+--- androidx.appcompat:appcompat:1.2.0
| +--- androidx.annotation:annotation:1.1.0
| +--- androidx.core:core:1.3.0 -> 1.3.2 (*)
| +--- androidx.cursoradapter:cursoradapter:1.0.0
| | \--- androidx.annotation:annotation:1.0.0 -> 1.1.0
| +--- androidx.fragment:fragment:1.1.0
| | +--- androidx.annotation:annotation:1.1.0
| | +--- androidx.core:core:1.1.0 -> 1.3.2 (*)
| | +--- androidx.collection:collection:1.1.0 (*)
| | +--- androidx.viewpager:viewpager:1.0.0
| | | +--- androidx.annotation:annotation:1.0.0 -> 1.1.0
| | | +--- androidx.core:core:1.0.0 -> 1.3.2 (*)
| | | \--- androidx.customview:customview:1.0.0
| | | +--- androidx.annotation:annotation:1.0.0 -> 1.1.0
| | | \--- androidx.core:core:1.0.0 -> 1.3.2 (*)
| | +--- androidx.loader:loader:1.0.0
| | | +--- androidx.annotation:annotation:1.0.0 -> 1.1.0
| | | +--- androidx.core:core:1.0.0 -> 1.3.2 (*)
| | | +--- androidx.lifecycle:lifecycle-livedata:2.0.0
| | | | +--- androidx.arch.core:core-runtime:2.0.0
| | | | | +--- androidx.annotation:annotation:1.0.0 -> 1.1.0
| | | | | \--- androidx.arch.core:core-common:2.0.0 -> 2.1.0 (*)
| | | | +--- androidx.lifecycle:lifecycle-livedata-core:2.0.0
| | | | | +--- androidx.lifecycle:lifecycle-common:2.0.0 -> 2.1.0 (*)
| | | | | +--- androidx.arch.core:core-common:2.0.0 -> 2.1.0 (*)
| | | | | \--- androidx.arch.core:core-runtime:2.0.0 (*)
| | | | \--- androidx.arch.core:core-common:2.0.0 -> 2.1.0 (*)
| | | \--- androidx.lifecycle:lifecycle-viewmodel:2.0.0 -> 2.1.0
| | | \--- androidx.annotation:annotation:1.1.0
| | +--- androidx.activity:activity:1.0.0
| | | +--- androidx.annotation:annotation:1.1.0
| | | +--- androidx.core:core:1.1.0 -> 1.3.2 (*)
| | | +--- androidx.lifecycle:lifecycle-runtime:2.1.0 (*)
| | | +--- androidx.lifecycle:lifecycle-viewmodel:2.1.0 (*)
| | | \--- androidx.savedstate:savedstate:1.0.0
| | | +--- androidx.annotation:annotation:1.1.0
| | | +--- androidx.arch.core:core-common:2.0.1 -> 2.1.0 (*)
| | | \--- androidx.lifecycle:lifecycle-common:2.0.0 -> 2.1.0 (*)
| | \--- androidx.lifecycle:lifecycle-viewmodel:2.0.0 -> 2.1.0 (*)
| +--- androidx.appcompat:appcompat-resources:1.2.0
| | +--- androidx.annotation:annotation:1.1.0
| | +--- androidx.core:core:1.0.1 -> 1.3.2 (*)
| | +--- androidx.vectordrawable:vectordrawable:1.1.0
| | | +--- androidx.annotation:annotation:1.1.0
| | | +--- androidx.core:core:1.1.0 -> 1.3.2 (*)
| | | \--- androidx.collection:collection:1.1.0 (*)
| | \--- androidx.vectordrawable:vectordrawable-animated:1.1.0
| | +--- androidx.vectordrawable:vectordrawable:1.1.0 (*)
| | +--- androidx.interpolator:interpolator:1.0.0
| | | \--- androidx.annotation:annotation:1.0.0 -> 1.1.0
| | \--- androidx.collection:collection:1.1.0 (*)
| \--- androidx.drawerlayout:drawerlayout:1.0.0
| +--- androidx.annotation:annotation:1.0.0 -> 1.1.0
| +--- androidx.core:core:1.0.0 -> 1.3.2 (*)
| \--- androidx.customview:customview:1.0.0 (*)
+--- com.google.android.material:material:1.3.0
| +--- androidx.annotation:annotation:1.0.1 -> 1.1.0
| +--- androidx.appcompat:appcompat:1.1.0 -> 1.2.0 (*)
| +--- androidx.cardview:cardview:1.0.0
| | \--- androidx.annotation:annotation:1.0.0 -> 1.1.0
| +--- androidx.coordinatorlayout:coordinatorlayout:1.1.0
| | +--- androidx.annotation:annotation:1.1.0
| | +--- androidx.core:core:1.1.0 -> 1.3.2 (*)
| | +--- androidx.customview:customview:1.0.0 (*)
| | \--- androidx.collection:collection:1.0.0 -> 1.1.0 (*)
| +--- androidx.constraintlayout:constraintlayout:2.0.1 -> 2.1.0
| +--- androidx.core:core:1.2.0 -> 1.3.2 (*)
| +--- androidx.dynamicanimation:dynamicanimation:1.0.0
| | +--- androidx.core:core:1.0.0 -> 1.3.2 (*)
| | +--- androidx.collection:collection:1.0.0 -> 1.1.0 (*)
| | \--- androidx.legacy:legacy-support-core-utils:1.0.0
| | +--- androidx.annotation:annotation:1.0.0 -> 1.1.0
| | +--- androidx.core:core:1.0.0 -> 1.3.2 (*)
| | +--- androidx.documentfile:documentfile:1.0.0
| | | \--- androidx.annotation:annotation:1.0.0 -> 1.1.0
| | +--- androidx.loader:loader:1.0.0 (*)
| | +--- androidx.localbroadcastmanager:localbroadcastmanager:1.0.0
| | | \--- androidx.annotation:annotation:1.0.0 -> 1.1.0
| | \--- androidx.print:print:1.0.0
| | \--- androidx.annotation:annotation:1.0.0 -> 1.1.0
| +--- androidx.annotation:annotation-experimental:1.0.0
| +--- androidx.fragment:fragment:1.0.0 -> 1.1.0 (*)
| +--- androidx.lifecycle:lifecycle-runtime:2.0.0 -> 2.1.0 (*)
| +--- androidx.recyclerview:recyclerview:1.0.0 -> 1.1.0
| | +--- androidx.annotation:annotation:1.1.0
| | +--- androidx.core:core:1.1.0 -> 1.3.2 (*)
| | +--- androidx.customview:customview:1.0.0 (*)
| | \--- androidx.collection:collection:1.0.0 -> 1.1.0 (*)
| +--- androidx.transition:transition:1.2.0
| | +--- androidx.annotation:annotation:1.1.0
| | +--- androidx.core:core:1.0.1 -> 1.3.2 (*)
| | \--- androidx.collection:collection:1.0.0 -> 1.1.0 (*)
| +--- androidx.vectordrawable:vectordrawable:1.1.0 (*)
| \--- androidx.viewpager2:viewpager2:1.0.0
| +--- androidx.annotation:annotation:1.1.0
| +--- androidx.fragment:fragment:1.1.0 (*)
| +--- androidx.recyclerview:recyclerview:1.1.0 (*)
| +--- androidx.core:core:1.1.0 -> 1.3.2 (*)
| \--- androidx.collection:collection:1.1.0 (*)
...
BUILD SUCCESSFUL in 1s
1.3.1、去除冲突依赖
当确定最终只保留的版本时,过滤掉其他版本,如下只保留ore:0.9.5.0
api("com.afollestad.material-dialogs:core:0.9.5.0") {
exclude group: 'com.android.support'
}
1.3.2、CompileOnly只编译不打包
当我们开发SDK时如果需要依赖第三方库,使用CompileOnly引用第三方库,而让使用SDK的开发者去决定选择哪个版本的第三方库,避免他人调用你的SDK时出现版本冲突
1.3.3、通过gradle脚本检测依赖库版本
待实现...
2、MultiDex分包
2.1、65535产生原因
默认情况下,Android工程代码编译后的.class文件会打包进一个dex中,dex中每一个方法会使用C++中的unsigned short类型的字段标记,unsigned short取值范围为0~65535,所以一旦方法数超过上限就会造成65536
2.2、分包
通过分包解决65535问题,根据minSdk版本不同分两种情况
2.2.1、minSdk>=21时分包
在app module下的build.gradle中设置multiDexEnabled=true即可
android {
compileSdk 30
defaultConfig {
applicationId "com.chenyangqi.gradle"
minSdk 21
targetSdk 30
versionCode 1
versionName "1.0"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
...
}
2.2.2、minSdk<21时分包
minSdk小于21时除了设置multidexEnabled=true还要引用androidx.multidex:multidex:2.0.1这个Google提供的分包库
android {
compileSdk 30
defaultConfig {
applicationId "com.chenyangqi.gradle"
minSdk 19
targetSdk 30
versionCode 1
versionName "1.0"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
...
}
dependencies {
implementation 'androidx.multidex:multidex:2.0.1'
...
}
然后再在application中继承分包库中的MultiDexApplication,又分三种情况
没有自定义Application时,直接在清单文件中注册name=MultiDexApplication
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.chenyangqi.gradle">
<application
android:name="androidx.multidex.MultiDexApplication"
...
</application>
</manifest>
有自定义Application时直接继承
class MyApplication:MultiDexApplication() {
}
当自定义Application已继承其他父类时重写attachBaseContext方法进行初始化
class MyApplication:OtherApplication() {
override fun onCreate() {
super.onCreate()
}
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
MultiDex.install(this)
}
}
3、代码混淆
3.1、开启混淆
buildTypes {
debug {
minifyEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
设置minifyEnabled=true和shrinkResources=true启用压缩、混淆和优化功能, proguard-android-optimize.txt为存放在SDK中Android默认的混淆规则,存放路径~/SDK/tools/proguard/proguard-android-optimize.txt,proguard-rules.pro为项目中自己定义的混淆规则
3.2、常用混淆规则
关键字 | 描述 |
---|---|
keep | 保留类和类中的成员,防止被混淆或者移除 |
keepnames | 保留类和类中的成员,防止被混淆,但是当成员没有被引用时会被移除 |
keepclassmembers | 只保留类中的成员,防止他们被混淆或者移除 |
keepclassmembersnames | 只保留类中的成员,防止他们被混淆或者移除,但是当类中的成员没有被引用时还是会被移除 |
keepclasseswithmembers | 保留类和类中的成员,前提是指明的类中必须含有该成员,没有的话还是会被混淆 |
keepclasseswithmembersnames | 保留类和类中的成员,前提是指明的类中必须含有该成员,没有的话还是会被混淆。需要注意的是没有被引用的成员会被移除 |
关键字 | 描述 |
---|---|
<filed> | 匹配类中的所有字段 |
<method> | 匹配类中的所有方法 |
<init> | 匹配类中的所有构造函数 |
* | 匹配任意长度字符,但不含包名分隔符(.)。比如说我们的完整类名是com.example.test.MyActivity,使用com.*,或者com.exmaple.*都是无法匹配的,因为*无法匹配包名中的分隔符,正确的匹配方式是com.exmaple.*.*,或者com.exmaple.test.*,这些都是可以的。但如果你不写任何其它内容,只有一个*,那就表示匹配所有的东西。 |
** | 匹配任意长度字符,并且包含包名分隔符(.)。比如proguard-android.txt中使用的-dontwarn android.support.**就可以匹配android.support包下的所有内容,包括任意长度的子包。 |
*** | 匹配任意参数类型。比如void set*(***)就能匹配任意传入的参数类型,*** get*()就能匹配任意返回值的类型。 |
… | 匹配任意长度的任意类型参数。比如void test(…)就能匹配任意void test(String a)或者是void test(int a, String b)这些方法。 |
3.3、Android常用混淆模板
#-------------------------------------------基本不用动区域--------------------------------------------
#---------------------------------基本指令区----------------------------------
-optimizationpasses 5
-dontskipnonpubliclibraryclassmembers
-printmapping proguardMapping.txt
-optimizations !code/simplification/cast,!field/*,!class/merging/*
-keepattributes *Annotation*,InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
#----------------------------------------------------------------------------
#---------------------------------默认保留区---------------------------------
#继承activity,application,service,broadcastReceiver,contentprovider....不进行混淆
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends androidx.multidex.MultiDexApplication
-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 * extends android.view.View
-keep class android.support.** {*;}
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
#这个主要是在layout 中写的onclick方法android:onclick="onClick",不进行混淆
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
-keep class **.R$* {
*;
}
-keepclassmembers class * {
void *(*Event);
}
#枚举
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#// natvie 方法不混淆
-keepclasseswithmembernames class * {
native <methods>;
}
#保持 Parcelable 不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#---------------------------------webview------------------------------------
-keepclassmembers class android.webkit.WebView {
public *;
}
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String);
}
#----------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------------
#---------------------------------实体类---------------------------------
#修改成你对应的包名
-keep class com.chenyangqi.gradle.proguard.bean.** { *; }
#---------------------------------第三方包-------------------------------
#---------------------------------反射相关的类和方法-----------------------
#---------------------------------与js互相调用的类------------------------
#---------------------------------自定义View的类------------------------