從零開始的Android新項目2 - Gradle篇

相信大家的項目用上gradle都已經很久了,但用得如何呢?這裡分享一下我的gradle腳本,大部分都是去年6月左右就開始用上的,有一部分比如簽名的安全保存則是最近才自己動手,做了令自己覺得還不錯的方案。

module類型的區分

科普小結,可能有些同學不太明白Java library module和Android library module是怎麼區分的,其實就是個plugin的區別,在module的build.gradle中:

Android application module:

apply plugin: com.android.applicationn

Android library module:

apply plugin: com.android.libraryn

Java library module:

apply plugin: javan

版本號管理

如果只有一個application module還好,如果我們有多個module怎麼辦呢?每次改版本號累不累?

解決方案就是在root里申明全局變數,可以在單獨的gradle里(比如新建一個dependency.gradle)申明然後apply from引用進來,或者直接定義在root的build.gradle中。

project.ext {n applicationId = "com.xxx"n buildToolsVersion = "23.0.2"n compileSdkVersion = 23n minSdkVersion = 14n targetSdkVersion = 23n versionCode = 1n versionName = "1.0.0"n abortOnLintError = falsen checkLintRelease = falsen useJack = falsen abortOnLintError = falsenn javaVersion = JavaVersion.VERSION_1_8n ...n}n

在子module裡面則使用rootProject.ext去進行引用:

android {n compileSdkVersion rootProject.ext.compileSdkVersionn buildToolsVersion rootProject.ext.buildToolsVersionnn defaultConfig {n applicationId rootProject.ext.applicationIdn minSdkVersion rootProject.ext.minSdkVersionn targetSdkVersion rootProject.ext.targetSdkVersionn versionCode rootProject.ext.versionCoden versionName rootProject.ext.versionNamen multiDexEnabled truen }nn compileOptions {n sourceCompatibility rootProject.ext.javaVersionn sourceCompatibility rootProject.ext.javaVersionn }nn packagingOptions {n exclude LICENSE.txtn exclude META-INF/DEPENDENCIESn exclude META-INF/ASL2.0n exclude META-INF/NOTICEn exclude META-INF/LICENSEn }nn lintOptions {n abortOnError rootProject.ext.abortOnLintErrorn checkReleaseBuilds rootProject.ext.checkLintReleasen quiet truen ignoreWarnings truen // Some libraries have issues with this.n disable InvalidPackagen // Lint gives this warning but SDK 20 would be Android L Beta.n disable OldTargetApin }n ...n}n

依賴管理

那麼多第三方庫的引用,在多個module里引用,修改起版本號來好辛苦,萬一有一個漏改了(比如gson)結果導致了異常行為,搞不好查原因查個半天,結果摔鍵盤竟然是版本號導致的。

so,和上節類似,我們需要統一定義依賴:

def daggerVersion = "2.0.2"ndef retrofitVersion = "2.0.0-beta4"ndef supportVersion = "23.2.1"ndef rxBindingVersion = 0.4.0nndef leakCanaryVersion = "1.3.1"ndef blockCanaryVersion = 1.1.4nnproject.ext {n ...n libSupportAppcompat = "com.android.support:appcompat-v7:${supportVersion}"n libSupportDesign = "com.android.support:design:${supportVersion}"n libSupportRecyclerview = "com.android.support:recyclerview-v7:${supportVersion}"n libSupportV4 = "com.android.support:support-v4:${supportVersion}"nn libRxAndroid = "io.reactivex:rxandroid:1.1.0"n libRxJava = "io.reactivex:rxjava:1.1.1"n libEventBus = "org.greenrobot:eventbus:3.0.0"n libJavaxAnnotation = "javax.annotation:jsr250-api:1.0"nn libGson = "com.google.code.gson:gson:2.4"n libRetrofit = "com.squareup.retrofit2:retrofit:${retrofitVersion}"n libRetrofitConverterGson = "com.squareup.retrofit2:converter-gson:${retrofitVersion}"n libRetrofitAdapterRxJava = "com.squareup.retrofit2:adapter-rxjava:${retrofitVersion}"n libOkHttpLoggingInterceptor = "com.squareup.okhttp3:logging-interceptor:3.0.0-RC1"nn libDagger = "com.google.dagger:dagger:${daggerVersion}"n libDaggerCompiler = "com.google.dagger:dagger-compiler:${daggerVersion}"nn libGlide = "com.github.bumptech.glide:glide:3.7.0"nn libRxBinding = "com.jakewharton.rxbinding:rxbinding:${rxBindingVersion}"n libRxBindingSupportV4 = "com.jakewharton.rxbinding:rxbinding-support-v4:${rxBindingVersion}"n libRxBindingAppcompatV7 = "com.jakewharton.rxbinding:rxbinding-appcompat-v7:${rxBindingVersion}"n libRxBindingDesign = "com.jakewharton.rxbinding:rxbinding-design:${rxBindingVersion}"n libRxBindingRecyclerview = "com.jakewharton.rxbinding:rxbinding-recyclerview-v7:${rxBindingVersion}"nn libRealm = "io.realm:realm-android:0.87.5"nn debugDependencies = [n leakCanary: "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}",n blockcanary: "com.github.moduth:blockcanary-ui:${blockCanaryVersion}",n ]nn releaseDependencies = [n leakCanary: "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryVersion}",n blockcanary: "com.github.moduth:blockcanary-no-op:${blockCanaryVersion}",n ]n}n

這裡也可以根據個人喜好把版本號也全都抽出去,我個人的實踐原則是除非引用超出1處,否則還是定義在一起。

module中使用:

dependencies {n compile fileTree(include: [*.jar], dir: libs)n ...n apt rootProject.ext.libDaggerCompilern compile rootProject.ext.libDaggern compile rootProject.ext.libRxJavan compile rootProject.ext.libRxAndroidn compile rootProject.ext.libRxBindingn compile rootProject.ext.libGliden provided rootProject.ext.libJavaxAnnotationn compile rootProject.ext.libSupportAppcompatn compile rootProject.ext.libSupportDesignn compile rootProject.ext.libSupportRecyclerviewn compile rootProject.ext.libSupportV4n debugCompile rootProject.ext.debugDependencies.leakCanaryn releaseCompile rootProject.ext.releaseDependencies.leakCanaryn debugCompile rootProject.ext.debugDependencies.blockCanaryn releaseCompile rootProject.ext.releaseDependencies.blockCanaryn}n

這裡我還特地為一些debug和release compile不同包的定義了2個map,見leakCanary和blockCanary引用。

簽名管理

簽名是一個很敏感的東西,只要有了簽名文件和對應的密碼信息,就能輕易反編譯修改源碼然後再簽名進行發布,因此如何保存這些敏感信息是很重要的。

在我的個人實踐中,主要做了這麼幾點:

local.properties定義keystore信息文件路徑:

keystore.props.file=../keystore.propertiesn

keystore.properties保存keystore信息:

store=../buildsystem/release.jksnalias=xxxnstorePass=xxxnpass=xxxn

buildsystem下保存了:

$ lsnci.gradlendebug.keystorenrelease.jksn

application module的signingConfigs:

signingConfigs {nn def Properties localProps = new Properties()n localProps.load(new FileInputStream(file(../local.properties)))n def Properties keyProps = new Properties()nn // 如果讀取不到keystore.props.file屬性,就使用debug keystorenn if (localProps[keystore.props.file]) {n keyProps.load(new FileInputStream(file(localProps[keystore.props.file])))n } else {n keyProps["store"] = ../buildsystem/debug.keystoren keyProps["alias"] = androidn keyProps["storePass"] = androiddebugkeyn keyProps["pass"] = androidn }nn debug {n storeFile file(keyProps["store"])n keyAlias keyProps["alias"]n storePassword keyProps["storePass"]n keyPassword keyProps["pass"]n }nn release {n // release版本使用assert確保存在該屬性否則報錯,避免錯誤打包n assert localProps[keystore.props.file];n storeFile file(keyProps["store"])n keyAlias keyProps["alias"]n storePassword keyProps["storePass"]n keyPassword keyProps["pass"]n }n}n

Java8支持

對Android的module

apply plugin: me.tatarka.retrolambdannandroid {n compileOptions {n sourceCompatibility rootProject.ext.javaVersionn sourceCompatibility rootProject.ext.javaVersionn }n}n

對Java的module:

sourceCompatibility = 1.8ntargetCompatibility = 1.8n

Split APK

詳細的可以看看Google的官方文檔Apk Splits

我的使用:

splits {n abi {n enable truen reset()n include armeabi, x86 //, x86, armeabi-v7a, mipsn universalApk falsen }n}n

大致來說,就是可以根據腳本的配置,將apk以abi、density進行分包。再也不用為了縮小包的體積而專門去只留下一個arm的jni文件夾了,想怎麼分怎麼分,搞不定哪天就要傳一個x86的包了,而且有的模擬器也只支持x86。

當然如果市場能支持這些配置,那就更好了,用戶下載apk的流量就小多了。

Module aar依賴

怎麼能在使用aar依賴提升編譯速度的同時,又能兼顧靈活性,隨時可以修改源碼呢?

解決方案就是module式aar依賴。

在你的library module目錄下, 打開build/outputs/aar,是不是有aar文件(編譯過後就會生成)?把它放到module目錄下面,然後在build.gradle裡面:

configurations.maybeCreate("default")nartifacts.add("default", file(lib_authorize-debug.aar))n

再把原來那些腳本給注釋了,就搞定了。是不是特別簡單?如果想再使用源碼依賴,反注釋一下就好了。

總結

本篇主要講了開發階段gradle的各種實踐,下一篇是什麼暫時我也沒有想法,哈哈。

歡迎加入QQ群:568863373。

歡迎關注我們的公眾號:魔都三帥,歡迎大家來投稿~只需要是未在微信平台上發布過的技術相關類文章都可以哦(不局限於任何語言和平台)。


推薦閱讀:

如何在做網上自動化賺錢項目?
國內有Android技術團隊在使用facebook的buck進行代碼構建嗎?
有什麼好的、實用性強的Gradle教程 或 經驗心得?
求比較好的gradle的入門指引?
在 AndroidStudio 工程點擊 Run 按鈕, 實際上做了什麼操作呢?

TAG:Android | Gradle | Android开发 |