productFlavors Android product flavor customization, multi-channel packaging configuration, application slimming

Android gradle provides productFlavors, which allows developers to customize and distinguish multiple product flavors. Common functions include configuring multiple channels, specifying different dimensions, specifying custom parameter strings, and specifying signatures.

Step 1: Usually, we need to preset the dimensions required by the product in the build. Gradle under the app in advance

//flavor(Product flavor)Configuration
    flavorDimensions "chip", "oem"  //This is the dimension,It is a descriptive vocabulary for a certain type of flavor.

Step 2: Under the dimension, we need to add all channels and corresponding dimensions, parameters, application signatures and other configurations.

For example, _chip_alight3399 the following figure is one of the channels included in the project, and dimension indicates the latitude of the corresponding channel. This item is required. If it is not filled in, an error will be reported. signingConfig It indicates the corresponding application signature used when the channel is packaged; buildConfigField Indicates some parameters to be configured. In the figure, the string of the storage path of mmkv is configured (added here according to the actual project requirements).

productFlavors {    //Product flavor customization

        _chip_alight3399 {  //Channel name
            dimension "chip"    //Specify dimensions(Required,Otherwise, an error will be reported)
            signingConfig signingConfigs.alight3399 //Signature configuration
            buildConfigField "String", "MMKV_PATH", "\"/sdcard/Android/data/com.alight.android.aoa_launcher/alight_mmkv\""
            //Custom string
        }
        _chip_3399 {
            dimension "chip"
            signingConfig signingConfigs.release_3399
            buildConfigField("String", "MMKV_PATH", "\"/data/media/0/alight_mmkv/\"")
        }

        _chip_3566 {
            dimension "chip"
            signingConfig signingConfigs.release_3566
            buildConfigField("String", "MMKV_PATH", "\"/sdcard/alight_mmkv/\"")

        }

        _oem_self_test {
            dimension "oem"
            buildConfigField("String", "API_HOST", "\"test.api.alight-sys.com\"")
        }

        _oem_self_pre {
            dimension "oem"
            buildConfigField("String", "API_HOST", "\"pre-api.alight-sys.com\"")
        }

        _oem_self_prod {
            dimension "oem"
            buildConfigField("String", "API_HOST", "\"api.alight-sys.com\"")
        }

        _oem_guangfeng_test {
            dimension "oem"
            buildConfigField("String", "API_HOST", "\"appotronics.api.alight-sys.com\"")
        }

        _oem_guangfeng_prod {
            dimension "oem"
            buildConfigField("String", "API_HOST", "\"appotronics.api.alight-sys.com\"")
        }

    }

The third step is to use applicationVariants to package the project to the specified path.

As shown in the following figure, it also contains some information such as the name and version number of the configuration application package (in actual development, it is still customized according to the actual project requirements).

//application variant copy to the corresponding location
    applicationVariants.all { variant ->
        def oldVersion = variant.versionName;
        String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
        def version_env = "test";
        if (tskReqStr.contains("_pre")) {
            version_env = "pre-release"
        } else if (tskReqStr.contains("_test")) {
            version_env = "test"
        } else if (tskReqStr.contains("_prod")) {
            version_env = "prod-release"
        }
        def versionName = oldVersion + "-" + version_env
        Pattern chip_pattern = Pattern.compile("_chip_(\\w+)(_)")
        Matcher chip_matcher = chip_pattern.matcher(tskReqStr)
        String chip = "3399"
        if (chip_matcher.find()) {
            chip = chip_matcher.group(1)
        }
        def name = "launcher_{chip}_{project.APP_VERSION_CODE}_{versionName}_{project.AOS_VERSION}_{releaseTime()}.apk"
        variant.outputs.each { output ->
            output.versionNameOverride = versionName;
        }
        variant.assemble.doLast {
            variant.outputs.forEach { file ->
                ant.copy file: file.outputFile,
                        tofile: "{project.rootDir}/launcher_build/$name"
            }
        }
    }

Supplement: Application Slimming

In buildTypes, you can configure minifyEnabled as true to start obfuscation when packaging the application. After obfuscation, some variables and method names will be converted into abbreviations, which reduces the size of the application package to a certain extent; At the same time, shrinkResources is often used together with minifyEnabled. When packaging, it will give priority to screening the file ID that is not used in the project, such as images, PDF files under raw, etc. Turn on shrinkResources (shrinkResources is set to true) to greatly reduce the size of the packaged application

buildTypes {//Build type Included by defaultdebug release Can also be customized
        release {
            minifyEnabled false //Whether to start obfuscation
            shrinkResources false //Clean up redundant image resources orrawResources not used in,Usually used together with confusion(and confusion can be achieved to a certain extentapkThe role of weight loss)
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            debuggable true

        }
        debug {
            signingConfig signingConfigs.debug
        }
    }

All code references of the above contents are as follows:

//flavor(Product flavor)Configuration
    flavorDimensions "chip", "oem"  //This is the dimension,It is a descriptive vocabulary for a certain type of flavor.
    productFlavors {    //Product flavor customization

        _chip_alight3399 {  //Channel name
            dimension "chip"    //Specify dimensions(Required,Otherwise, an error will be reported)
            signingConfig signingConfigs.alight3399 //Signature configuration
            buildConfigField "String", "MMKV_PATH", "\"/sdcard/Android/data/com.alight.android.aoa_launcher/alight_mmkv\""
            //Custom string
        }
        _chip_3399 {
            dimension "chip"
            signingConfig signingConfigs.release_3399
            buildConfigField("String", "MMKV_PATH", "\"/data/media/0/alight_mmkv/\"")
        }

        _chip_3566 {
            dimension "chip"
            signingConfig signingConfigs.release_3566
            buildConfigField("String", "MMKV_PATH", "\"/sdcard/alight_mmkv/\"")

        }

        _oem_self_test {
            dimension "oem"
            buildConfigField("String", "API_HOST", "\"test.api.alight-sys.com\"")
        }

        _oem_self_pre {
            dimension "oem"
            buildConfigField("String", "API_HOST", "\"pre-api.alight-sys.com\"")
        }

        _oem_self_prod {
            dimension "oem"
            buildConfigField("String", "API_HOST", "\"api.alight-sys.com\"")
        }

        _oem_guangfeng_test {
            dimension "oem"
            buildConfigField("String", "API_HOST", "\"appotronics.api.alight-sys.com\"")
        }

        _oem_guangfeng_prod {
            dimension "oem"
            buildConfigField("String", "API_HOST", "\"appotronics.api.alight-sys.com\"")
        }

    }

    //application variant copy to the corresponding location
    applicationVariants.all { variant ->
        def oldVersion = variant.versionName;
        String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
        def version_env = "test";
        if (tskReqStr.contains("_pre")) {
            version_env = "pre-release"
        } else if (tskReqStr.contains("_test")) {
            version_env = "test"
        } else if (tskReqStr.contains("_prod")) {
            version_env = "prod-release"
        }
        def versionName = oldVersion + "-" + version_env
        Pattern chip_pattern = Pattern.compile("_chip_(\\w+)(_)")
        Matcher chip_matcher = chip_pattern.matcher(tskReqStr)
        String chip = "3399"
        if (chip_matcher.find()) {
            chip = chip_matcher.group(1)
        }
        def name = "launcher_{chip}_{project.APP_VERSION_CODE}_{versionName}_{project.AOS_VERSION}_{releaseTime()}.apk"
        variant.outputs.each { output ->
            output.versionNameOverride = versionName;
        }
        variant.assemble.doLast {
            variant.outputs.forEach { file ->
                ant.copy file: file.outputFile,
                        tofile: "{project.rootDir}/launcher_build/$name"
            }
        }
    }
}