Android apk打包那些事

1、如何在app级别的gradle.build文件中增加自动签名

在android {}中增加以下配置

 // 增加自动签名的内容
    signingConfigs {
        config {
            keyAlias KEY_ALIAS
            keyPassword KEY_PASSWORD
            storeFile file(STORE_FILE)
            storePassword STORE_PASSWORD
        }
    }
    buildTypes {
        debug {
            // 增加签名设置
            signingConfig signingConfigs.config
        }
        release {
            // 增加签名设置
            signingConfig signingConfigs.config
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

我们预先在gradle.properties文件中藏好了签名的信息:

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

然后在signingConfigs{} 中引用了gradle.properties中的签名信息。
之后在buildTypes{} 中增加签名设置。
这样一来,构建出来的apk文件就自动打上了签名。

2、如何根据不同的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 是否开启根据ABI生成多个apk;
  • reset() 清空ABI列表;
  • include 设置具体需要的ABI;
  • universalApk false 是否生成一个包含所有ABI的apk文件。

设置好之后,双击击构建脚本,就可以生成不同ABI的apk文件。


image.png

3、如何修改构建出的apk文件名

在app级别的gradle.build文件的android {}中添加下面代码:

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

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

4、如何实现多渠道打包

假如现在我一套代码需要打包成如图所示的四个apk文件:


image.png

它们分别是应用名“accountbook”、“notebook”和蓝色启动图标、红色启动图标的组合。

4.1、定义风格维度

在app级别的gradle.build文件的android {}中定义风格维度。有多少项就会有多少维度。下面定义了'appName', 'iconColor'两个维度。

flavorDimensions 'appName', 'iconColor'
4.2、定义每个维度对应有多少风格

如本例中appName 有 accountbook 和 notebook 两种风格;iconColor 有 redIcon 和 blueIcon 两种风格。
最终编译出来的产品,就是两个维度风格的组合。
如本例中共有accountbookredIcon、accountbookblueIcon、notebookredIcon、notebookblueIcon四种产品。

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、得到每个产品的构建脚本

点击sync之后,就可以在androidstudio的右边脚本栏的build中看到每一个产品对应的构建脚本。


image.png
4.4、如何将具体的某一个apk安装到手机、模拟器

此时点击Androidstudio的工具栏的绿色三角,安装的是上面组合中的第一个组合的apk。


image.png

想要安装具体某一个的apk,可以在右边脚本栏的install脚本中找到具体脚本。点击想安装的即可安装到手机、模拟器。


image.png
4.5、怎么做到将差异资源分别打包到各个产品apk

上面的步骤,虽然已经可以打包得到四个apk文件,但是它们的名称和启动图标都是一样的。如何做到将差异资源打包到四个apk中呢?
记得我们做多语言适配、横竖屏布局文件的做法吧?这里是类似的。

  • 4.5.1、我们首先在app目录下创建与main文件夹同级的文件夹,名称分别是我们再productFlavors{}中定义的四种productFlavor。


    image.png
  • 4.5.2、appName维度我们需要不同的应用名称,所以我们在accountbook和notebook中创建和main同路径同文件名的strings.xml


    image.png

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


    image.png

    image.png

    通过上面的步骤,我们就可以打包出不同应用名的apk了。
  • 4.5.3 在redicon和blueicon中创建启动图标。
    image.png

    不要看到上面这么多mipmap的目录就觉得很复杂,其实就是在redIcon中创建了红色启动图标、在blueIcon中创建了蓝色启动图标而已。(复习一下启动图标相关知识?)
    创建好的图标名称依然叫ic_launcher,和main中保持一致。

经过上面的工作,我们现在就可以构建出不同应用名不同启动图标的apk了。


image.png

安装到手机上,就是下面的样子:


image.png
4.6、差异代码怎么办

上面的做法只是实现了差异资源的打包。那如果我每个产品还有差异代码呢,这时候该怎么办?
其实,编译生成的BuildConfig类就包含了我们的productFlavors的信息。我们可以在代码中判断当前属于哪一个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的拼接问题

再来看一下,我们的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'
        }
    }

我们为每一个productFlavor都定义了applicationId、versionCode、applicationIdSuffix 、versionName 、versionNameSuffix 。
最后会拼接处什么样的applicationId和versionName 呢?
在Androidstudio中双击其中一个apk文件,自动打开apk分析器,点击里面的AndroidManifest.xml,可以看到拼接结果。


image.png

这个package应该就是拼接后的applicationId吧。不信的话我们再打开output-metadata.json文件。


image.png

从结果可以看出,

  • 只有第一个维度的applicationId、versionCode、versionName会完整拼接,后面维度的都被忽略了。
  • 所有维度applicationIdSuffix 、versionNameSuffix ,都会被拼接。

所以,我们保留第一维度的applicationId、versionCode、versionName,其它维度的applicationId、versionCode、versionName删除;
而第一维度的各种后缀似乎并不需要,所以将它们删除。
总的来说就是第一维度定义前缀,后面维度定义后缀。
假如我们修改成下面的样子:

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}第一个风格维度包含的名称,在这里值是accountbook或notebook;
{productFlavors[1].name}第二个风格维度包含的名称,在这里值是redIcon或blueIcon;
{buildType.name}是构建类型,即debug或release;
{abiName}就是我们定义的不同ABI的名称;
{productFlavors[0].versionName}第一个风格维度的versionName,即accountbook或notebook的versionName;
{productFlavors[1].versionNameSuffix}第二个风格维度的versionNameSuffix,即redIcon或blueIcon的versionNameSuffix 。

如此一来,我们就可以构建出不同应用名、不同启动图标所对应不同ABI的apk文件,而且apk文件的名称分别对应的信息所拼成。


image.png

5、最后

最后贴上以上设置的所有内容:

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"
    }

    // 增加自动签名的内容
    signingConfigs {
        config {
            keyAlias KEY_ALIAS
            keyPassword KEY_PASSWORD
            storeFile file(STORE_FILE)
            storePassword STORE_PASSWORD
        }
    }
    buildTypes {
        debug {
            // 增加签名设置
            signingConfig signingConfigs.config
        }
        release {
            // 增加签名设置
            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'
        }
    }
}

你可能感兴趣的:(Android apk打包那些事)