相信大家的项目用上gradle都已经很久了,但用得如何呢?这里分享一下我的gradle脚本,大部分都是去年6月左右就开始用上的,有一部分比如签名的安全保存则是最近才自己动手,做了令自己觉得还不错的方案。
module类型的区分
科普小结,可能有些同学不太明白Java library module和Android library module是怎么区分的,其实就是个plugin的区别,在module的build.gradle中:
Android application module:
1 | apply plugin: 'com.android.application' |
Android library module:
1 | apply plugin: 'com.android.library' |
Java library module:
1 | apply plugin: 'java' |
版本号管理
如果只有一个application module还好,如果我们有多个module怎么办呢?每次改版本号累不累?
解决方案就是在root里申明全局变量,可以在单独的gradle里(比如新建一个dependency.gradle)申明然后apply from引用进来,或者直接定义在root的build.gradle中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | project.ext { applicationId = "com.xxx" buildToolsVersion = "23.0.2" compileSdkVersion = 23 minSdkVersion = 14 targetSdkVersion = 23 versionCode = 1 versionName = "1.0.0" abortOnLintError = false checkLintRelease = false useJack = false abortOnLintError = false javaVersion = JavaVersion.VERSION_1_8 ... } |
在子module里面则使用rootProject.ext去进行引用:
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 | android { compileSdkVersion rootProject.ext.compileSdkVersion buildToolsVersion rootProject.ext.buildToolsVersion defaultConfig { applicationId rootProject.ext.applicationId minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode rootProject.ext.versionCode versionName rootProject.ext.versionName multiDexEnabled true } compileOptions { sourceCompatibility rootProject.ext.javaVersion sourceCompatibility rootProject.ext.javaVersion } packagingOptions { exclude 'LICENSE.txt' exclude 'META-INF/DEPENDENCIES' exclude 'META-INF/ASL2.0' exclude 'META-INF/NOTICE' exclude 'META-INF/LICENSE' } lintOptions { abortOnError rootProject.ext.abortOnLintError checkReleaseBuilds rootProject.ext.checkLintRelease quiet true ignoreWarnings true // Some libraries have issues with this. disable 'InvalidPackage' // Lint gives this warning but SDK 20 would be Android L Beta. disable 'OldTargetApi' } ... } |
依赖管理
那么多第三方库的引用,在多个module里引用,修改起版本号来好辛苦,万一有一个漏改了(比如gson)结果导致了异常行为,搞不好查原因查个半天,结果摔键盘竟然是版本号导致的。
so,和上节类似,我们需要统一定义依赖:
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 | def daggerVersion = "2.0.2" def retrofitVersion = "2.0.0-beta4" def supportVersion = "23.2.1" def rxBindingVersion = '0.4.0' def leakCanaryVersion = "1.3.1" def blockCanaryVersion = '1.1.4' project.ext { ... libSupportAppcompat = "com.android.support:appcompat-v7:${supportVersion}" libSupportDesign = "com.android.support:design:${supportVersion}" libSupportRecyclerview = "com.android.support:recyclerview-v7:${supportVersion}" libSupportV4 = "com.android.support:support-v4:${supportVersion}" libRxAndroid = "io.reactivex:rxandroid:1.1.0" libRxJava = "io.reactivex:rxjava:1.1.1" libEventBus = "org.greenrobot:eventbus:3.0.0" libJavaxAnnotation = "javax.annotation:jsr250-api:1.0" libGson = "com.google.code.gson:gson:2.4" libRetrofit = "com.squareup.retrofit2:retrofit:${retrofitVersion}" libRetrofitConverterGson = "com.squareup.retrofit2:converter-gson:${retrofitVersion}" libRetrofitAdapterRxJava = "com.squareup.retrofit2:adapter-rxjava:${retrofitVersion}" libOkHttpLoggingInterceptor = "com.squareup.okhttp3:logging-interceptor:3.0.0-RC1" libDagger = "com.google.dagger:dagger:${daggerVersion}" libDaggerCompiler = "com.google.dagger:dagger-compiler:${daggerVersion}" libGlide = "com.github.bumptech.glide:glide:3.7.0" libRxBinding = "com.jakewharton.rxbinding:rxbinding:${rxBindingVersion}" libRxBindingSupportV4 = "com.jakewharton.rxbinding:rxbinding-support-v4:${rxBindingVersion}" libRxBindingAppcompatV7 = "com.jakewharton.rxbinding:rxbinding-appcompat-v7:${rxBindingVersion}" libRxBindingDesign = "com.jakewharton.rxbinding:rxbinding-design:${rxBindingVersion}" libRxBindingRecyclerview = "com.jakewharton.rxbinding:rxbinding-recyclerview-v7:${rxBindingVersion}" libRealm = "io.realm:realm-android:0.87.5" debugDependencies = [ leakCanary: "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}", blockcanary: "com.github.moduth:blockcanary-ui:${blockCanaryVersion}", ] releaseDependencies = [ leakCanary: "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryVersion}", blockcanary: "com.github.moduth:blockcanary-no-op:${blockCanaryVersion}", ] } |
这里也可以根据个人喜好把版本号也全都抽出去,我个人的实践原则是除非引用超出1处,否则还是定义在一起。
module中使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') ... apt rootProject.ext.libDaggerCompiler compile rootProject.ext.libDagger compile rootProject.ext.libRxJava compile rootProject.ext.libRxAndroid compile rootProject.ext.libRxBinding compile rootProject.ext.libGlide provided rootProject.ext.libJavaxAnnotation compile rootProject.ext.libSupportAppcompat compile rootProject.ext.libSupportDesign compile rootProject.ext.libSupportRecyclerview compile rootProject.ext.libSupportV4 debugCompile rootProject.ext.debugDependencies.leakCanary releaseCompile rootProject.ext.releaseDependencies.leakCanary debugCompile rootProject.ext.debugDependencies.blockCanary releaseCompile rootProject.ext.releaseDependencies.blockCanary } |
这里我还特地为一些debug和release compile不同包的定义了2个map,见leakCanary和blockCanary引用。
签名管理
签名是一个很敏感的东西,只要有了签名文件和对应的密码信息,就能轻易反编译修改源码然后再签名进行发布,因此如何保存这些敏感信息是很重要的。
在我的个人实践中,主要做了这么几点:
local.properties定义keystore信息文件路径:
1 | keystore.props.file=../keystore.properties |
keystore.properties保存keystore信息:
1 2 3 4 | store=../buildsystem/release.jks alias=xxx storePass=xxx pass=xxx |
buildsystem下保存了:
1 2 3 4 | $ ls ci.gradle debug.keystore release.jks |
application module的signingConfigs:
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 | signingConfigs { def Properties localProps = new Properties() localProps.load(new FileInputStream(file('../local.properties'))) def Properties keyProps = new Properties() // 如果读取不到'keystore.props.file'属性,就使用debug keystore if (localProps['keystore.props.file']) { keyProps.load(new FileInputStream(file(localProps['keystore.props.file']))) } else { keyProps["store"] = '../buildsystem/debug.keystore' keyProps["alias"] = 'android' keyProps["storePass"] = 'androiddebugkey' keyProps["pass"] = 'android' } debug { storeFile file(keyProps["store"]) keyAlias keyProps["alias"] storePassword keyProps["storePass"] keyPassword keyProps["pass"] } release { // release版本使用assert确保存在该属性否则报错,避免错误打包 assert localProps['keystore.props.file']; storeFile file(keyProps["store"]) keyAlias keyProps["alias"] storePassword keyProps["storePass"] keyPassword keyProps["pass"] } } |
Java8支持
对Android的module
1 2 3 4 5 6 7 8 | apply plugin: 'me.tatarka.retrolambda' android { compileOptions { sourceCompatibility rootProject.ext.javaVersion sourceCompatibility rootProject.ext.javaVersion } } |
对Java的module:
1 2 | sourceCompatibility = 1.8 targetCompatibility = 1.8 |
Split APK
详细的可以看看Google的官方文档Apk Splits
我的使用:
1 2 3 4 5 6 7 8 | splits { abi { enable true reset() include 'armeabi', 'x86' //, 'x86', 'armeabi-v7a', 'mips' universalApk false } } |
大致来说,就是可以根据脚本的配置,将apk以abi、density进行分包。再也不用为了缩小包的体积而专门去只留下一个arm的jni文件夹了,想怎么分怎么分,搞不定哪天就要传一个x86的包了,而且有的模拟器也只支持x86。
当然如果市场能支持这些配置,那就更好了,用户下载apk的流量就小多了。
Module aar依赖
怎么能在使用aar依赖提升编译速度的同时,又能兼顾灵活性,随时可以修改源码呢?
解决方案就是module式aar依赖。
在你的library module目录下, 打开build/outputs/aar,是不是有aar文件(编译过后就会生成)?把它放到module目录下面,然后在build.gradle里面:
1 2 | configurations.maybeCreate("default") artifacts.add("default", file('lib_authorize-debug.aar')) |
再把原来那些脚本给注释了,就搞定了。是不是特别简单?如果想再使用源码依赖,反注释一下就好了。
总结
本篇主要讲了开发阶段gradle的各种实践,下一篇是什么暂时我也没有想法,哈哈。