1、统一版本库管理 1.1、统一版本号管理 创建一个gradle文件统一管理 不同module下的第三方库和其他属性的配置参数 如下,在项目根目录创建config.gradle 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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
1 apply from: project.file('config.gradle' )
1.2、local.perporties使用场景 一些不便对外展示的敏感参数可以定义在local.properties中,如一些付费库的key,maven仓库的用户名和密码等
1 2 3 sdk.dir=/Users/ mutou/Library/ Android/sdk username=chenyangqi password=123456
编译时通过如下方式获取local.properties中定义的属性
1 2 3 4 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
1 2 3 4 5 6 7 8 9 10 11 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,命令如下
1 ./gradlew :app:dependencies --configuration oppoNormalProdReleaseCompileClasspath
依赖树如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 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
1 2 3 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即可
1 2 3 4 5 6 7 8 9 10 11 12 13 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提供的分包库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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
1 2 3 4 5 6 7 <manifest xmlns:android ="http://schemas.android.com/apk/res/android" package ="com.chenyangqi.gradle" > <application android:name ="androidx.multidex.MultiDexApplication" ... </application > </manifest >
有自定义Application时直接继承
1 2 class MyApplication :MultiDexApplication () {}
当自定义Application已继承其他父类时重写attachBaseContext方法进行初始化
1 2 3 4 5 6 7 8 9 10 11 class MyApplication :OtherApplication () { override fun onCreate () { super .onCreate() } override fun attachBaseContext (base: Context ?) { super .attachBaseContext(base) MultiDex.install(this ) } }
3、代码混淆 3.1、开启混淆 1 2 3 4 5 6 7 8 9 10 11 12 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常用混淆模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 #-------------------------------------------基本不用动区域-------------------------------------------- #---------------------------------基本指令区---------------------------------- -optimizationpasses 5 -dontskipnonpubliclibraryclassmembers -printmapping proguardMapping.txt -optimizations !code/simplification/ cast,!field
4、APK签名 如下,对release包进行签名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 android { ... signingConfigs { release { keyAlias 'chenyangqi' keyPassword '123456' storeFile file('../app_key.jks' ) storePassword '123456' } } buildTypes { debug { minifyEnabled false shrinkResources false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt' ), 'proguard-rules.pro' } release { signingConfig signingConfigs.release minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt' ), 'proguard-rules.pro' } } ... }
有些情况下生产jks时会出现如下警告提示,按照提示迁移jks格式即可,命令如下
1 keytool -importkeystore -srckeystore ./app_key.jks -destkeystore ./app_key.jks -deststoretype pkcs12
5、APK构建变体 在一些情况下需要在一个android工程需要输出不同的APK,比如为了照顾到低端机型运行流畅度,推出极速版APP,例如今日头条极速版,抖音极速版等,或者是免费版和VIP付费版APP等,所以需要构建APK变体
5.1 flavorDimensions和productFlavors 使用flavorDimensions定义要输出的APK的维度,比如这个栗子中的version和environment,version表示APP极速版和普通版这个维度,environment表示APP是生产环境还是系统集成测试环境,然后再通过productFlavors定义极速版和普通版的dimension为version,sit环境和prod环境的dimension为environment,两种维度排列组合后可以生成 极速版sit环境.apk、极速版prod环境.apk、普通版sit环境.apk、普通版prod环境.apk
不要使用中文,这里只是举栗子便于理解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 android { ... flavorDimensions "version" , "environment" productFlavors { 极速版 { dimension "version" } 普通版 { dimension "version" } sit环境 { dimension "environment" } prod环境 { dimension "environment" } } ... }
在左下角Build Variants面板选择你需要编译的apk变体版本
针对不同版本的APK可以定制不同的特性,比如applicationId,applicationName,logo等,也可以通创建BuildConfig的常亮值进行一些版本定制,如下例举了常用熟悉设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 android { flavorDimensions "version" , "environment" productFlavors { speed { dimension "version" versionNameSuffix ".speed" applicationIdSuffix ".speed" manifestPlaceholders = [ logo : "@mipmap/ic_launcher_speed" , appName: "GradleDemo极速版" ] buildConfigField('boolean' ,"isSpeed" ,'true' ) } normal { dimension "version" manifestPlaceholders = [ logo : "@mipmap/ic_launcher" , appName: "GradleDemo" ] buildConfigField('boolean' ,"isSpeed" ,'false' ) } sit { dimension "environment" versionNameSuffix ".sit" applicationIdSuffix ".sit" applicationIdSuffix ".sit" buildConfigField('String' ,'baseUrl' ,'"https://sit..."' ) } prod { dimension "environment" buildConfigField('String' ,'baseUrl' ,'"https://prod..."' ) } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } }
通过manifestPlaceholders定义了logo和appName,然后在manifests设置如下,就可以修改各组的logo和APP名字
1 2 3 4 5 6 7 8 9 10 <application android:name =".MyApplication" android:allowBackup ="true" android:icon ="${logo}" android:label ="${appName}" android:roundIcon ="${logo}" android:supportsRtl ="true" android:theme ="@style/Theme.GradleDemo" > ... </application >
5.2、定义BilddConfig常量
buildConfigField(‘String’,’baseUrl’,’”https://sit..."' )
通过buildConfigField方法,可以作为代码逻辑中区分不同版本业务区分的依据,如上定义了测试环境的BaseUrl,对应生成的BuildConfig如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public final class BuildConfig { public static final boolean DEBUG = Boolean.parseBoolean("true" ); public static final String APPLICATION_ID = "com.chenyangqi.gradle.speed.sit" ; public static final String BUILD_TYPE = "debug" ; public static final String FLAVOR = "speedSit" ; public static final String FLAVOR_version = "speed" ; public static final String FLAVOR_environment = "sit" ; public static final int VERSION_CODE = 1 ; public static final String VERSION_NAME = "1.0.speed.sit" ; public static final String baseUrl = "https://sit..." ; public static final boolean isSpeed = true ; }
以上例举了常用的apk变体属性修改操作,更多的属性参考官方文档
6、多渠道打包 多渠道打包跟上面讲到的APK构建变体其实差不多,都是为了满足业务需求构建不同版本的apk,比如为了满足运营统计需要统计不同手机厂商渠道APP用户数量,我们一般会针对不同的引用市场发布不同的APK包,以oppo vivo 小米应用市场为例,首先使用meta-dta标签在manifest定义渠道标,然后增加一个flavorDimensions维度为channel如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <application android:name =".MyApplication" android:allowBackup ="true" android:icon ="${logo}" android:label ="${appName}" android:roundIcon ="${logo}" android:supportsRtl ="true" android:theme ="@style/Theme.GradleDemo" > <meta-data android:name ="MTA_CHANNEL" android:value ="${MTA_CHANNEL_VALUE}" /> ... </application >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 flavorDimensions "channel" , "version" , "environment" productFlavors { oppo { dimension "channel" manifestPlaceholders = [MTA_CHANNEL_VALUE: "oppo" ] } vivo { dimension "channel" manifestPlaceholders = [MTA_CHANNEL_VALUE: "vivo" ] } xiaomi { dimension "channel" manifestPlaceholders = [MTA_CHANNEL_VALUE: "xiaomi" ] } ... }
执行./gradlew assembleRelease 命令就能够打包出对呀的渠道包,并生成对应的MTA_CHANNEL作为渠道统计的标识
修改生产APK名字,其中variant.productFlavors表示flavorDimensions定义的几种维度
1 2 3 4 5 6 7 8 9 10 11 12 applicationVariants.all { variant -> variant.outputs.each { output -> if (variant.buildType.name == 'release' ) { def fileName = "GradleDemo_${variant.productFlavors[0].name}_" + "${variant.productFlavors[1].name}_" + "${variant.productFlavors[2].name}_" + "${defaultConfig.versionName}" + "_release.apk" output.outputFileName = fileName } } }
修改apk名字后如下图所示
在运行时获取meta-data方法如下
1 2 val appInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)val channelName = appInfo.metaData.getString("MTA_CHANNEL" )
7、BuildConfig使用 BuildConfig可以在编译器生成常量字段和资源文件字段,如下在buildTypes下定义了一个BuildConfig.BUILD_TIME常量和资源resource变量title
1 2 3 4 5 6 7 8 9 10 11 12 buildTypes { debug { ... buildConfigField("String" , "BUILD_TIME" , "" ${System.currentTimeMillis()}"" ) resValue("String" , "title" , "debug首页标题" ) } release { ... buildConfigField("String" , "BUILD_TIME" , "" ${System.currentTimeMillis()}"" ) resValue("String" , "title" , "release首页标题" ) } }
生成的BuildConfig如下
1 2 3 4 5 public final class BuildConfig { ... public static final String VERSION_NAME = "1.0.0" ; ... }
生成的资源属性如下
8、构建速度优化 8.1、构建速度优化方案
及时更新Gradle版本
构建minSDK>21的开发期变体APK,minSDK<21时需要做更多的版本适配和分包会相对耗时 如下,在sit系统集成测试包下设置minSDK=26
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 productFlavors { ... sit { dimension "environment" versionNameSuffix ".sit" applicationIdSuffix ".sit" applicationIdSuffix ".sit" buildConfigField('String' , 'baseUrl' , '"https://sit..."' ) minSdkVersion 26 } prod { dimension "environment" buildConfigField('String' , 'baseUrl' , '"https://prod..."' ) } }
使用本地缓存,开启Gradle离线模式,避免每次构建时从maven仓库查询是否有新版本
4. 组件化开发
8.2、使用profile分析构建性能 使用Gradle profile工具分析构建过程,点击右侧大象图标,输入如下命令,其中offline为关闭网络,rerun-tasks为重新执行所有任务,–profile为生成分析文件
1 gradle :app:assembleOppoNormalProdRelease --offline --rerun-tasks --profile
执行完命令后会在项目的根目录下的Build/reports/目录下生成分析报告的一个html
在浏览器打开后能看到总耗时,各个task耗时等指标
本文为博主原创,转载请注明出处