Android apk打包那些事

1、如何在app級(jí)別的gradle.build文件中增加自動(dòng)簽名

在android {}中增加以下配置

 // 增加自動(dòng)簽名的內(nèi)容
    signingConfigs {
        config {
            keyAlias KEY_ALIAS
            keyPassword KEY_PASSWORD
            storeFile file(STORE_FILE)
            storePassword STORE_PASSWORD
        }
    }
    buildTypes {
        debug {
            // 增加簽名設(shè)置
            signingConfig signingConfigs.config
        }
        release {
            // 增加簽名設(shè)置
            signingConfig signingConfigs.config
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

我們預(yù)先在gradle.properties文件中藏好了簽名的信息:

KEY_ALIAS=test
KEY_PASSWORD=test
STORE_FILE=D\:\\Android\\sinatrue\\test_sign.jks
STORE_PASSWORD=test

然后在signingConfigs{} 中引用了gradle.properties中的簽名信息。
之后在buildTypes{} 中增加簽名設(shè)置。
這樣一來(lái),構(gòu)建出來(lái)的apk文件就自動(dòng)打上了簽名。

2、如何根據(jù)不同的ABI生成不同的apk

在gradle.build文件中的android {}中增加以下配置

    splits {

        // Configures multiple APKs based on ABI.
        abi {

            // Enables building multiple APKs per ABI.
            enable true

            // By default all ABIs are included, so use reset() and include to specify that we only
            // want APKs for x86 and x86_64.

            // Resets the list of ABIs that Gradle should create APKs for to none.
            reset()

            // Specifies a list of ABIs that Gradle should create APKs for.
            include "x86", "x86_64", "arm64-v8a", "armeabi-v7a"

            // Specifies that we do not want to also generate a universal APK that includes all ABIs.
            universalApk false
        }
    }
  • enable true 是否開(kāi)啟根據(jù)ABI生成多個(gè)apk;
  • reset() 清空ABI列表;
  • include 設(shè)置具體需要的ABI;
  • universalApk false 是否生成一個(gè)包含所有ABI的apk文件。

設(shè)置好之后,雙擊擊構(gòu)建腳本,就可以生成不同ABI的apk文件。


image.png

3、如何修改構(gòu)建出的apk文件名

在app級(jí)別的gradle.build文件的android {}中添加下面代碼:

    android.applicationVariants.all {
        variant ->
            variant.outputs.all { output ->
                outputFileName = "xxx.apk"
            }
    }

"xxx"就是最終得到的文件名,文件格式是apk。

4、如何實(shí)現(xiàn)多渠道打包

假如現(xiàn)在我一套代碼需要打包成如圖所示的四個(gè)apk文件:


image.png

它們分別是應(yīng)用名“accountbook”、“notebook”和藍(lán)色啟動(dòng)圖標(biāo)、紅色啟動(dòng)圖標(biāo)的組合。

4.1、定義風(fēng)格維度

在app級(jí)別的gradle.build文件的android {}中定義風(fēng)格維度。有多少項(xiàng)就會(huì)有多少維度。下面定義了'appName', 'iconColor'兩個(gè)維度。

flavorDimensions 'appName', 'iconColor'
4.2、定義每個(gè)維度對(duì)應(yīng)有多少風(fēng)格

如本例中appName 有 accountbook 和 notebook 兩種風(fēng)格;iconColor 有 redIcon 和 blueIcon 兩種風(fēng)格。
最終編譯出來(lái)的產(chǎn)品,就是兩個(gè)維度風(fēng)格的組合。
如本例中共有accountbookredIcon、accountbookblueIcon、notebookredIcon、notebookblueIcon四種產(chǎn)品。

productFlavors {
        accountbook {
            dimension 'appName'
            applicationId 'com.stranger.accountbook'
            applicationIdSuffix '.endaccountbook'
            versionCode 66
            versionName 'a1'
            versionNameSuffix 'a2'
        }
        notebook {
            dimension 'appName'
            applicationId 'com.stranger.notebook'
            applicationIdSuffix '.endnotebook'
            versionCode 77
            versionName 'n1'
            versionNameSuffix 'n2'
        }
        redIcon {
            dimension 'iconColor'
            applicationId 'com.stranger.redIcon'
            applicationIdSuffix '.endredIcon'
            versionCode 88
            versionName 'r1'
            versionNameSuffix 'r2'
        }
        blueIcon {
            dimension 'iconColor'
            applicationId 'com.stranger.blueIcon'
            applicationIdSuffix '.endblueIcon'
            versionCode 99
            versionName 'b1'
            versionNameSuffix 'b2'
        }
    }
4.3、得到每個(gè)產(chǎn)品的構(gòu)建腳本

點(diǎn)擊sync之后,就可以在androidstudio的右邊腳本欄的build中看到每一個(gè)產(chǎn)品對(duì)應(yīng)的構(gòu)建腳本。


image.png
4.4、如何將具體的某一個(gè)apk安裝到手機(jī)、模擬器

此時(shí)點(diǎn)擊Androidstudio的工具欄的綠色三角,安裝的是上面組合中的第一個(gè)組合的apk。


image.png

想要安裝具體某一個(gè)的apk,可以在右邊腳本欄的install腳本中找到具體腳本。點(diǎn)擊想安裝的即可安裝到手機(jī)、模擬器。


image.png
4.5、怎么做到將差異資源分別打包到各個(gè)產(chǎn)品apk

上面的步驟,雖然已經(jīng)可以打包得到四個(gè)apk文件,但是它們的名稱(chēng)和啟動(dòng)圖標(biāo)都是一樣的。如何做到將差異資源打包到四個(gè)apk中呢?
記得我們做多語(yǔ)言適配、橫豎屏布局文件的做法吧?這里是類(lèi)似的。

  • 4.5.1、我們首先在app目錄下創(chuàng)建與main文件夾同級(jí)的文件夾,名稱(chēng)分別是我們?cè)賞roductFlavors{}中定義的四種productFlavor。


    image.png
  • 4.5.2、appName維度我們需要不同的應(yīng)用名稱(chēng),所以我們?cè)赼ccountbook和notebook中創(chuàng)建和main同路徑同文件名的strings.xml


    image.png

    然后分別在它們中定義app_name。注意name和main中的保持一致,都是“app_name”。


    image.png

    image.png

    通過(guò)上面的步驟,我們就可以打包出不同應(yīng)用名的apk了。
  • 4.5.3 在redicon和blueicon中創(chuàng)建啟動(dòng)圖標(biāo)。
    image.png

    不要看到上面這么多mipmap的目錄就覺(jué)得很復(fù)雜,其實(shí)就是在redIcon中創(chuàng)建了紅色啟動(dòng)圖標(biāo)、在blueIcon中創(chuàng)建了藍(lán)色啟動(dòng)圖標(biāo)而已。(復(fù)習(xí)一下啟動(dòng)圖標(biāo)相關(guān)知識(shí)?
    創(chuàng)建好的圖標(biāo)名稱(chēng)依然叫ic_launcher,和main中保持一致。

經(jīng)過(guò)上面的工作,我們現(xiàn)在就可以構(gòu)建出不同應(yīng)用名不同啟動(dòng)圖標(biāo)的apk了。


image.png

安裝到手機(jī)上,就是下面的樣子:


image.png
4.6、差異代碼怎么辦

上面的做法只是實(shí)現(xiàn)了差異資源的打包。那如果我每個(gè)產(chǎn)品還有差異代碼呢,這時(shí)候該怎么辦?
其實(shí),編譯生成的BuildConfig類(lèi)就包含了我們的productFlavors的信息。我們可以在代碼中判斷當(dāng)前屬于哪一個(gè)productFlavor,從而做出差異化處理。

 if (BuildConfig.FLAVOR_appName == "accountbook" && BuildConfig.FLAVOR_iconColor == "redIcon") {
            //do something different.
        }
        if (BuildConfig.FLAVOR_appName == "accountbook" && BuildConfig.FLAVOR_iconColor == "blueIcon") {
            //do something different.
        }
        if (BuildConfig.FLAVOR_appName == "notebook" && BuildConfig.FLAVOR_iconColor == "redIcon") {
            //do something different.
        }
        if (BuildConfig.FLAVOR_appName == "notebook" && BuildConfig.FLAVOR_iconColor == "blueIcon") {
            //do something different.
        }
4.7、applicationId和versionName的拼接問(wèn)題

再來(lái)看一下,我們的productFlavors是這樣定義的:

productFlavors {
        accountbook {
            dimension 'appName'
            applicationId 'com.stranger.accountbook'
            applicationIdSuffix '.endaccountbook'
            versionCode 66
            versionName 'a1'
            versionNameSuffix 'a2'
        }
        notebook {
            dimension 'appName'
            applicationId 'com.stranger.notebook'
            applicationIdSuffix '.endnotebook'
            versionCode 77
            versionName 'n1'
            versionNameSuffix 'n2'
        }
        redIcon {
            dimension 'iconColor'
            applicationId 'com.stranger.redIcon'
            applicationIdSuffix '.endredIcon'
            versionCode 88
            versionName 'r1'
            versionNameSuffix 'r2'
        }
        blueIcon {
            dimension 'iconColor'
            applicationId 'com.stranger.blueIcon'
            applicationIdSuffix '.endblueIcon'
            versionCode 99
            versionName 'b1'
            versionNameSuffix 'b2'
        }
    }

我們?yōu)槊恳粋€(gè)productFlavor都定義了applicationId、versionCode、applicationIdSuffix 、versionName 、versionNameSuffix 。
最后會(huì)拼接處什么樣的applicationId和versionName 呢?
在A(yíng)ndroidstudio中雙擊其中一個(gè)apk文件,自動(dòng)打開(kāi)apk分析器,點(diǎn)擊里面的AndroidManifest.xml,可以看到拼接結(jié)果。


image.png

這個(gè)package應(yīng)該就是拼接后的applicationId吧。不信的話(huà)我們?cè)俅蜷_(kāi)output-metadata.json文件。


image.png

從結(jié)果可以看出,

  • 只有第一個(gè)維度的applicationId、versionCode、versionName會(huì)完整拼接,后面維度的都被忽略了。
  • 所有維度applicationIdSuffix 、versionNameSuffix ,都會(huì)被拼接。

所以,我們保留第一維度的applicationId、versionCode、versionName,其它維度的applicationId、versionCode、versionName刪除;
而第一維度的各種后綴似乎并不需要,所以將它們刪除。
總的來(lái)說(shuō)就是第一維度定義前綴,后面維度定義后綴。
假如我們修改成下面的樣子:

productFlavors {
        accountbook {
            dimension 'appName'
            applicationId 'com.stranger.accountbook'
            versionCode 66
            versionName 'a1'
        }
        notebook {
            dimension 'appName'
            applicationId 'com.stranger.notebook'
            versionCode 77
            versionName 'n1'
        }
        redIcon {
            dimension 'iconColor'
            applicationIdSuffix '.endredIcon'
            versionNameSuffix 'r2'
        }
        blueIcon {
            dimension 'iconColor'
            applicationIdSuffix '.endblueIcon'
            versionNameSuffix 'b2'
        }
    }

我們生成的apk文件名也修改一下:

    android.applicationVariants.all {
        variant ->
            variant.outputs.all { output ->
                def abiName = output.getFilter(com.android.build.OutputFile.ABI)
                outputFileName = "${productFlavors[0].name}-${productFlavors[1].name}-$buildType.name-${abiName}-${productFlavors[0].versionName}-${productFlavors[1].versionNameSuffix}.apk"
            }
    }

{productFlavors[0].name}第一個(gè)風(fēng)格維度包含的名稱(chēng),在這里值是accountbook或notebook;
{productFlavors[1].name}第二個(gè)風(fēng)格維度包含的名稱(chēng),在這里值是redIcon或blueIcon;
{buildType.name}是構(gòu)建類(lèi)型,即debug或release;
{abiName}就是我們定義的不同ABI的名稱(chēng);
{productFlavors[0].versionName}第一個(gè)風(fēng)格維度的versionName,即accountbook或notebook的versionName;
{productFlavors[1].versionNameSuffix}第二個(gè)風(fēng)格維度的versionNameSuffix,即redIcon或blueIcon的versionNameSuffix 。

如此一來(lái),我們就可以構(gòu)建出不同應(yīng)用名、不同啟動(dòng)圖標(biāo)所對(duì)應(yīng)不同ABI的apk文件,而且apk文件的名稱(chēng)分別對(duì)應(yīng)的信息所拼成。


image.png

5、最后

最后貼上以上設(shè)置的所有內(nèi)容:

plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.2"

    defaultConfig {
        applicationId "com.stranger.testflavor"
        minSdkVersion 16
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    // 增加自動(dòng)簽名的內(nèi)容
    signingConfigs {
        config {
            keyAlias KEY_ALIAS
            keyPassword KEY_PASSWORD
            storeFile file(STORE_FILE)
            storePassword STORE_PASSWORD
        }
    }
    buildTypes {
        debug {
            // 增加簽名設(shè)置
            signingConfig signingConfigs.config
        }
        release {
            // 增加簽名設(shè)置
            signingConfig signingConfigs.config
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    splits {

        // Configures multiple APKs based on ABI.
        abi {

            // Enables building multiple APKs per ABI.
            enable true

            // By default all ABIs are included, so use reset() and include to specify that we only
            // want APKs for x86 and x86_64.

            // Resets the list of ABIs that Gradle should create APKs for to none.
            reset()

            // Specifies a list of ABIs that Gradle should create APKs for.
            include "x86", "x86_64", "arm64-v8a", "armeabi-v7a"

            // Specifies that we do not want to also generate a universal APK that includes all ABIs.
            universalApk false
        }
    }

    android.applicationVariants.all {
        variant ->
            variant.outputs.all { output ->
                def abiName = output.getFilter(com.android.build.OutputFile.ABI)
                outputFileName = "${productFlavors[0].name}-${productFlavors[1].name}-$buildType.name-${abiName}-${productFlavors[0].versionName}-${productFlavors[1].versionNameSuffix}.apk"
            }
    }

    flavorDimensions 'appName', 'iconColor'
    productFlavors {
        accountbook {
            dimension 'appName'
            applicationId 'com.stranger.accountbook'
            versionCode 66
            versionName 'a1'
        }
        notebook {
            dimension 'appName'
            applicationId 'com.stranger.notebook'
            versionCode 77
            versionName 'n1'
        }
        redIcon {
            dimension 'iconColor'
            applicationIdSuffix '.endredIcon'
            versionNameSuffix 'r2'
        }
        blueIcon {
            dimension 'iconColor'
            applicationIdSuffix '.endblueIcon'
            versionNameSuffix 'b2'
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容