Gradle(三) Gradle配置構(gòu)建和渠道包

1. 前言

Gradle系列已完成,專注于Gradle,有如下幾篇文章

Android開發(fā),打包的時候可能會打內(nèi)測包,外側(cè)包,release包等,還有就是有時候還需要打不同渠道的包等.這時它們里面的包名,應(yīng)用圖標,應(yīng)用名稱,某些資源文件,某些java文件等可能不同,如果通過人工去手動改,改了之后再打包的話,那就太麻煩了.現(xiàn)在有了Gradle,它可以幫到我們.

ps: 請先搞懂Android DSL的基本配置,比如compileSdkVersion是什么本文不會再介紹.有需要則查官方文檔,還有就是皇叔寫的寫給Android開發(fā)的Gradle知識體系非常不錯.

demo源碼: GradleStudy

2. 統(tǒng)一配置

2.1 以前的配置方式

今天我來帶大家實現(xiàn)一種很方便的配置項目諸如compileSdkVersion,三方庫引入等.最終的效果如下,可以直接通過Config點出來,并且還可以通過Ctrl+鼠標左鍵點過去.

QQempF.png

以前,很多很多項目會將一些基本的配置放到Project的build.gradle中,類似

ext {
    compileSdkVersion = 29
    buildToolsVersion = "29.0.0"
    targetSdkVersion = 29
    minSdkVersion = 21
    versionCode = 1
    versionName = "1.0.0"
}

然后在各個module的build.gradle中進行使用這個配置

android {
    compileSdkVersion rootProject.compileSdkVersion
    defaultConfig {
        versionCode rootProject.versionCode
        versionName rootProject.versionName
        minSdkVersion rootProject.minSdkVersion
        targetSdkVersion rootProject.targetSdkVersion
    }
}

這種方式可以,但是不夠優(yōu)雅.我們寫好rootProject,然后再輸入"."的時候AS不會提示你有哪些可用的變量,不能智能提示.而且即使你一字不差的寫好了,用Ctrl+鼠標左鍵也點不過去.在ext{}下的那些變量,你用快捷鍵搜索在哪些地方使用到了,AS也不知道....是不是覺得差點意思.

2.2 推薦的配置方式

ps: 這種配置方式,最開始是看到柯基大佬在使用,覺得太棒了,哈哈.這種配置方式,好像只能是3.5+版本的AS

我們來實現(xiàn)一種更優(yōu)雅的方式,實現(xiàn)上面的功能.創(chuàng)建一個buildSrc這個名字的module,這個module的名稱必須為buildSrc.因為我們創(chuàng)建的這個module是AS專門用來寫插件的,會自動參與編譯.創(chuàng)建好之后刪除Android那一堆東西,什么java代碼,res,清單文件等.只剩下build.gradle和.gitignore

QQleEj.png

把build.gradle文件內(nèi)容改成

repositories {
    google()
    jcenter()
}
apply {
    plugin 'groovy'
    plugin 'java-gradle-plugin'
}
dependencies {
    implementation gradleApi()
    implementation localGroovy()
    implementation "commons-io:commons-io:2.6"
}

然后在main下面創(chuàng)建文件夾groovy,sync一下.沒啥問題的話,應(yīng)該能編譯過.然后在groovy文件夾下面創(chuàng)建Config.groovy文件

class Config {

    static applicationId = 'com.xfhy.gradledemo'
    static appName = 'GradleDemo'
    static compileSdkVersion = 29
    static buildToolsVersion = '29.0.2'
    static minSdkVersion = 22
    static targetSdkVersion = 29
    static versionCode = 1
    static versionName = '1.0.0'

}

可以看到,我們將常用配置全部填入這里.這個時候去module的build.gradle將這些參數(shù)全部替換掉.

android {
    compileSdkVersion Config.compileSdkVersion
    buildToolsVersion Config.buildToolsVersion
    defaultConfig {
        applicationId Config.applicationId
        minSdkVersion Config.minSdkVersion
        targetSdkVersion Config.targetSdkVersion
        versionCode Config.versionCode
        versionName Config.versionName
    }
    ....
}

完美.同理,將三方庫也可以加進來

class Config {
    static depConfig = [
            support      : [
                    appcompat_androidx   : "androidx.appcompat:appcompat:$appcompat_androidx_version",
                    recyclerview_androidx: "androidx.recyclerview:recyclerview:$recyclerview_androidx_version",
                    design               : "com.google.android.material:material:$design_version",
                    multidex             : "com.android.support:multidex:$multidex_version",
                    constraint           : "com.android.support.constraint:constraint-layout:$constraint_version",
            ],
            kotlin       : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version",
            leakcanary   : [
                    android         : "com.squareup.leakcanary:leakcanary-android:$leakcanary_version",
                    android_no_op   : "com.squareup.leakcanary:leakcanary-android-no-op:$leakcanary_version",
                    support_fragment: "com.squareup.leakcanary:leakcanary-support-fragment:$leakcanary_version",
            ],
    ]
}

在build.gradle中使用

dependencies {
    implementation Config.depConfig.support.recyclerview_androidx
    ....
}

3. 渠道包

3.1 productFlavors

productFlavors直譯為產(chǎn)品風味,Android這邊用它來做多渠道.在app的build.gradle中加入如下配置

android {
    flavorDimensions "channel"
    productFlavors {
        free {
            dimension "channel"
            //程序包名
            applicationId "com.xfhy.free"
            //替換清單文件中的標簽
            manifestPlaceholders = [
                    APP_ICON: "@drawable/ic_launcher",
                    APP_NAME: "xx免費版",
            ]
            //versionName
            versionName "2.0.0"
            //versionCode
            versionCode 2
        }
        vip {
            dimension "channel"
            //程序包名
            applicationId "com.xfhy.vip"
            //替換清單文件中的標簽
            manifestPlaceholders = [
                    APP_ICON: "@drawable/ic_launcher",
                    APP_NAME: "xxVip版",
            ]
            //versionName
            versionName "3.0.0"
            //versionCode
            versionCode 3
        }
        svip {
            dimension "channel"
        }
    }
}

如代碼所示,我們配置了3種類型的風味,在productFlavors中可以配置包名(applicationId)、版本號(versionCode)拭抬、版本名(versionName)、icon帽蝶、應(yīng)用名.并且可以在里面配置各種你之前在defaultConfig里面配置的東西.還可以配置src代碼目錄,res目錄之類的.并且這個時候Build Variants里面有了多種類型,比如:freeDebug,freeRelease,vipDebug,vipRelease等.你在Build Variants里面選擇freeDebug,則是使用free風味,并且是debug時使用的配置.

<application
    xmlns:tools="http://schemas.android.com/tools"
    android:icon="${APP_ICON}"
    android:label="${APP_NAME}"
    android:theme="@style/AppTheme"
    android:largeHeap="true"
    tools:replace="android:label">
    ...
</application>

3.2 渠道變量

首先來介紹一個關(guān)鍵詞擴展:applicationVariants,它是在AppExtension里面的,它的官方文檔,它意思是返回應(yīng)用程序項目包含的構(gòu)建變體的集合,是用all關(guān)鍵詞進行遍歷.我們拿到了這些變體之后,可以根據(jù)當前是哪個變體來構(gòu)建出相應(yīng)變體所特殊的變量.比如內(nèi)測和外測它們的地址肯定不一樣的,那么通過這種方式可以很方便地整出來.構(gòu)建的變量會存在于相應(yīng)的BuildConfig中,然后在java代碼中直接引用就行,替換地址時也不需要動java代碼,只需在gradle中改一下,然后它編譯的時候就會自動構(gòu)建BuildConfig,自動將地址搞成最新的了.說了這些多,show me the code!

android {
    applicationVariants.all { variant ->
        //構(gòu)建變體專屬變量
        switch (variant.flavorName) {
            case 'free':
                buildConfigField("String", "BASE_URL", "\"http://31.13.66.23\"")
                buildConfigField("String", "TOKEN", "\"dhaskufguakfaskfkjasjhbfree\"")
                break
            case 'vip':
                buildConfigField("String", "BASE_URL", "\"http://31.13.66.24\"")
                buildConfigField("String", "TOKEN", "\"dhaskfagafkjasjhbvip\"")
                break
            case 'svip':
                buildConfigField("String", "BASE_URL", "\"http://31.13.66.25\"")
                buildConfigField("String", "TOKEN", "\"dhaskufgufgsdagajasjhbsvip\"")
                break
        }
    }
}

將上面的代碼寫在app的build.gradle中,在上面的gradle代碼中我們定義了2個變量,不同的變體會構(gòu)建不同的值,比如上面的BASE_URL我們會在free變體編譯的時候就會在BuildConfig生成一個變量,值是http://31.13.66.23.我們來看一下BuildConfig中是些什么內(nèi)容:

//build\generated\source\buildConfig\free\debug\com\xfhy\gradledemo\BuildConfig.java
public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String APPLICATION_ID = "com.xfhy.free";
  public static final String BUILD_TYPE = "debug";
  public static final String FLAVOR = "free";
  public static final int VERSION_CODE = 2;
  public static final String VERSION_NAME = "2.0.0";
  // Fields from the variant
  public static final String BASE_URL = "http://31.13.66.23";
  public static final String TOKEN = "dhaskufguakfaskfkjasjhbfree";
}

這個文件是gradle構(gòu)建時自動為我們創(chuàng)建的,不需要去修改.我們構(gòu)建的變體變量在最下面,這里面的值確實是我們在gradle代碼中寫的那樣.這個里面已經(jīng)有一些不是我們搞出來的變量了,比如是否是DEBUG,APPLICATION_ID,VERSION_CODE之類的.我們在java代碼中使用的時候,直接BuildConfig.BASE_URL這種方式進行使用即可,它就是一個普通的java類,里面定義了一些變量而已.

當然除了上面的渠道變量之外,還有一些變量是公用的,每個變體都是一樣的那種.我們可以寫到defaultConfig下面.

android {
    defaultConfig {
        buildConfigField("String", "APP_DESCRIPTION", "\"你沒有見過的船新版本\"")
        buildConfigField("String[]", "TAB", "{\"首頁\",\"排行榜\",\"我的\"}")
    }
}

3.3 打包文件命名

還是利用上面的applicationVariants,當我們拿到了變體之后,在打包的時候動態(tài)的將打包之后的文件名改一下.比如改成下面這種形式

applicationVariants.all { variant ->
    variant.outputs.all {
        def type = variant.buildType.name
        def channel = variant.flavorName
        outputFileName = "demo_${variant.versionName}_${channel}_${type}.apk"
    }
}

最后它打出來的包是這樣的demo_2.0.0_free_debug.apk,寫完之后可以使用gradlew assembleFreeDebug命令試一下.命令運行之后會在app\build\outputs\apk\free\debug目錄下產(chǎn)生相應(yīng)的apk文件.

3.4 簽名

可以在gradle中指定打包時的簽名文件,密碼啥的

signingConfigs {
    debug {
        storeFile file('../keys/xfhy.jks')
        storePassword "qqqqqq"
        keyAlias "xfhy"
        keyPassword "qqqqqq"
        v1SigningEnabled true
        v2SigningEnabled true
    }
    release {
        storeFile file('../keys/xfhy.jks')
        storePassword "qqqqqq"
        keyAlias "xfhy"
        keyPassword "qqqqqq"
        v1SigningEnabled true
        v2SigningEnabled true
    }
}

指定了簽名以及密碼之后,打包的時候就只需要在命令行執(zhí)行gradlew assembleVipRelease即可,不用打開Android Studio了.

3.5 資源

Android Studio提供了代碼整合功能.只需要創(chuàng)建app/src/xxFlavorName/assets,app/src/xxFlavorName/src,app/src/xxFlavorName/res即可.當在Build Variants中切換切換變體之后,AS就只會編譯對應(yīng)變體的資源+main下面的資源.

可以看到,free下面的文件夾自動變色了,這些是free變體特殊的東西,只有在free編譯的時候才會被用到.java代碼,res資源等,到時是需要和main下面的一起合并的.

假如我在free變體下創(chuàng)建了Test.java,然后可以在main下面引用到,就和平時使用一樣.但是如果相同包名下如果free中有Test.java,main中也有,那么是編譯不過的. 還有就是當main里面用到了Test.java的時候,在Build Variants中切換成了vip,而vip中剛好沒有Test.java,就會報錯的,因為找不到這個文件.

上面這個問題,可以用sourceSets來解決,sourceSets可以指定代碼資源文件的位置.雖然上面創(chuàng)建的free,vip等變體文件夾下面也是放這些東西的,但是用sourceSets比他們優(yōu)先級高.

下面來看它的普通用法,一看就懂.

sourceSets {
    main {
        manifest.srcFile 'AndroidManifest.xml'
        java.srcDirs = ['src']
        aidl.srcDirs = ['src']
        renderscript.srcDirs = ['src']
        res.srcDirs = ['res']
        assets.srcDirs = ['assets']
    }
}

然后我們除了在src/main/java下有java代碼,還可以指定在其他地方有java代碼.比如下面這樣.可以在src下面創(chuàng)建common/java文件夾,用于存放公共的代碼.

sourceSets {
    sourceSets.main.java.srcDirs = ['src/main/java', 'src/common/java']
}

上面的Test.java問題,可以用sourceSets解決.在common文件夾創(chuàng)建一個公共的Test.java,然后其他變體可以使用.在free在使用自己特殊的Test.java,只拿給free用.

項目的結(jié)果是這樣的,這是變體是vip的時候:

sourceSets {
    main {
        java.srcDirs = ['src/main/java']
    }
    free {
        java.srcDirs = ['src/free/java']
    }

    svip {
        java.srcDirs = ['src/common/java']
    }

    vip {
        java.srcDirs = ['src/common/java']
    }
}

4. 總結(jié)

又學到了一大波干貨內(nèi)容.對于渠道包,可能不一定會用得到,但是其實還是挺有用的. 同一套代碼可以產(chǎn)出多個app,俗稱馬甲包,可能很多公司都在搞這種.如果用得上,希望能幫到你.

參考:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市恍箭,隨后出現(xiàn)的幾起案子衡未,更是在濱河造成了極大的恐慌晚顷,老刑警劉巖庞呕,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件新翎,死亡現(xiàn)場離奇詭異,居然都是意外死亡住练,警方通過查閱死者的電腦和手機地啰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來讲逛,“玉大人亏吝,你說我怎么就攤上這事≌祷欤” “怎么了蔚鸥?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長括饶。 經(jīng)常有香客問我,道長来涨,這世上最難降的妖魔是什么图焰? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮蹦掐,結(jié)果婚禮上技羔,老公的妹妹穿的比我還像新娘僵闯。我一直安慰自己,他們只是感情好藤滥,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布鳖粟。 她就那樣靜靜地躺著,像睡著了一般拙绊。 火紅的嫁衣襯著肌膚如雪向图。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天标沪,我揣著相機與錄音榄攀,去河邊找鬼。 笑死金句,一個胖子當著我的面吹牛檩赢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播违寞,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼贞瞒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了趁曼?” 一聲冷哼從身側(cè)響起军浆,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎彰阴,沒想到半個月后瘾敢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡尿这,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年簇抵,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片射众。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡碟摆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出叨橱,到底是詐尸還是另有隱情典蜕,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布罗洗,位于F島的核電站愉舔,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏伙菜。R本人自食惡果不足惜轩缤,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧火的,春花似錦壶愤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至湃累,卻和暖如春勃救,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背脱茉。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工剪芥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人琴许。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓税肪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親榜田。 傳聞我的和親對象是個殘疾皇子益兄,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 上一章我們學習了Gralde的使用,創(chuàng)建和轉(zhuǎn)換Android工程箭券。本章我們將深入了解構(gòu)建文件净捅,學習一些有用的tas...
    sollian閱讀 1,368評論 0 3
  • 文章來源:Google 此頁面以配置構(gòu)建概覽為基礎(chǔ),向您介紹如何配置構(gòu)建變體辩块,以便從同一個項目中創(chuàng)建應(yīng)用的不同版本...
    文文太遠了閱讀 760評論 0 1
  • 前言 最近遇到了問題蛔六,大概是 APPT2 ERROR 錯誤,這個錯誤很常見废亭,說的是 .9圖片 有問題国章,但是網(wǎng)上的...
    xiongmao_123閱讀 1,341評論 0 0
  • 1.介紹 如果你正在查閱build.gradle文件的所有可選項,請點擊這里進行查閱:DSL參考 1.1新構(gòu)建系統(tǒng)...
    Chuckiefan閱讀 12,121評論 8 72
  • 本文參加#青春不一YOUNG#征稿活動豆村,本人承諾液兽,文章內(nèi)容為原創(chuàng),且未在其他平臺發(fā)表過掌动。 你有沒有做過一些事情四啰,本...
    我叫李思佳閱讀 392評論 2 3