Gradle multi-channel packaging (dynamically setting App name, applying icons, replacing constants, changing package name, changing channels)

Keywords: Android xml Gradle Google

from: http://www.jianshu.com/p/533240d222d3

Recently, there is a requirement to package nine types of App s at a time, and both constants and String.xml have variables. Although variables have always existed before, they are only packaged one at a time. This allowed me to pack nine at a time. If I pack it nine times in the future, I'll be crazy.
According to previous understanding, gradle should be able to solve this problem. So I studied it carefully.

First put a complete multi-channel/multi-environment packaging configuration, and then explain.

Achieved:

  1. Different environment, different package names;
  2. Modify different string.xml resource files in different environments.
  3. Modify the specified constants in different environments.
  4. Modify channel variables in Android Manifest. XML in different environments
apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion '22.0.1'

    // Signature file
    signingConfigs {
        config {
            keyAlias 'lyl'
            keyPassword '123456'
            storeFile file('../lyl.jks')
            storePassword '123456'
        }
    }

    // Default configuration
    defaultConfig {
        //applicationId "com.lyl.app"
        minSdkVersion 16
        targetSdkVersion 22
        versionCode 1
        versionName "1.0.0"

        signingConfig signingConfigs.config
        multiDexEnabled true
    }

    // Different configurations of multi-channel/multi-environment
    productFlavors {
        dev {
            // Each environment package name is different
            applicationId "com.lyl.dev"
            // Add string.xml field dynamically;
            // Note that this is the addition, in string.xml can not have this field, will rename!!!
            resValue "string", "app_name", "dev_myapp"
            resValue "bool", "isrRank", 'false'
            // Dynamic modification of constant fields
            buildConfigField "String", "ENVIRONMENT", '"dev"'
            // Modify channel variables in Android Manifest. XML
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "dev"]
        }
        stage {
            applicationId "com.lyl.stage"

            resValue "string", "app_name", "stage_myapp"
            resValue "bool", "isrRank", 'true'

            buildConfigField "String", "ENVIRONMENT", '"stage"'

            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "stage"]
        }
        prod {
            applicationId "com.lyl.prod"

            resValue "string", "app_name", "myapp"
            resValue "bool", "isrRank", 'true'

            buildConfigField "String", "ENVIRONMENT", '"prod"'

            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "prod"]
        }
    }

    //Remove error from lint detection
    lintOptions {
        abortOnError false
    }

    def releaseTime() {
        return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
    }

    buildTypes {
        debug {
            signingConfig signingConfigs.config
        }

        release {
            buildConfigField("boolean", "LOG_DEBUG", "false")
            minifyEnabled false
            zipAlignEnabled true
            //Remove useless resource files
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.config

            // Batch packing
            applicationVariants.all { variant ->
                variant.outputs.each { output ->
                    def outputFile = output.outputFile
                    if (outputFile != null && outputFile.name.endsWith('.apk')) {
                        //The output APK name is: channel name_version name_time.apk
                        def fileName = "${variant.productFlavors[0].name}_v${defaultConfig.versionName}_${releaseTime()}.apk"
                        output.outputFile = new File(outputFile.parent, fileName)
                    }
                }
            }
        }
    }
}

repositories {
    mavenCentral()
}

dependencies {
    compile 'com.facebook.android:facebook-android-sdk:4.0.0'
    compile project(':qrscan')
    compile 'com.android.support:appcompat-v7:22.0.0'
    compile 'com.google.code.gson:gson:2.3'
    compile files('libs/android-async-http-1.4.6.jar')
    compile 'com.google.android.gms:play-services:7.5.0'
    compile 'com.android.support:support-annotations:22.1.1'
    compile 'com.github.bumptech.glide:glide:3.7.0'
    compile 'de.hdodenhof:circleimageview:2.1.0'
}

Next, let's look at modifying specific fields in detail.

The settings of different environments are basically set in product Flavors.
And you can add as many environments as you want.

1. Different environments and package names;

productFlavors {
    dev {
        applicationId "com.lyl.dev"
    }

    stage {
        applicationId "com.lyl.stage"
    }

    prod {
        applicationId "com.lyl.prod"
    }
}

Note here that in defaultConfig, you should all write a default application Id.
After testing, the different environment package names set by product Flavors will override the settings in defaultConfig.
So we can infer that the order of execution should be default first, and then sub-channel. If conflict occurs, it will override processing, which is also very logical.


2. Add string.xml resource files in different environments;

Using resValue to define the value of a resource, as the name implies, the content under res should be created, and finally referenced with R.xxx.xxx.
As follows, different app_name fields are added according to different types, and Boolean values are defined, which can be referenced by R.string.app_name.

Note that here is the addition, which is the addition of a field app_name in string.xml, so you can't have this field in the existing string.xml, otherwise it will be wrong!!!
productFlavors {
    dev {
        resValue "string", "app_name", "dev_myapp"
        resValue "bool", "isrRank", 'false'
    }
    stage {
        resValue "string", "app_name", "stage_myapp"
        resValue "bool", "isrRank", 'true'
    }
    prod {
        resValue "string", "app_name", "myapp"
        resValue "bool", "isrRank", 'true'
    }
}

From the above, we can probably infer that color and dimen can also be added in a similar way.


3. Dynamic modification of specified constants in different environments;

Use BuildConfig variables.

Definition field

When we define the following fields, the compiled files are automatically generated in the app/build/source/BuildConfig/dev/com.lyl.dev/BuildConfig directory.
Open this file and we can see the fields we defined.

productFlavors {
    dev {
        buildConfigField "String", "ENVIRONMENT", '"dev"'
    }
    stage {
        buildConfigField "String", "ENVIRONMENT", '"stage"'
    }
    prod {
        buildConfigField "String", "ENVIRONMENT", '"prod"'
    }
}
(2) Reference fields

In any of our own classes, we can call the fields we defined directly through BuildConfig.

public class Constants {
    public static final String ENVIRONMENT = BuildConfig.ENVIRONMENT;

}

Note: Here's a little detail. The third parameter is to use "'" first and then "". This grammar may be unfamiliar in Java, but in many other languages, this usage is very common.
It means "dev" as a whole belongs to a string. As for why you write this, you remove the single quotation marks and go to the file app/build/source/BuildConfig/dev/com.lyl.dev/BuildConfig to see.


4. Modify channel variables in Android Manifest. XML in different environments

(1) Add channel variables to Android Manifest. XML
<application
    android:icon="${app_icon}"
    android:label="@string/app_name"
    android:theme="@style/AppTheme">
    ...
    <meta-data
        android:name="UMENG_CHANNEL"
        android:value="${ENVIRONMENT}" />
    ...
</application>
(2) Setting up product Flavors in build.gradle
productFlavors {
    dev {
        manifestPlaceholders = [ENVIRONMENT: "dev",
                                app_icon   : "@drawable/icon_dev"]
    }
    stage {
        manifestPlaceholders = [ENVIRONMENT: "stage",
                                app_icon   : "@drawable/icon_stage"]
    }
    prod {
        manifestPlaceholders = [ENVIRONMENT: "prod",
                                app_icon   : "@drawable/icon_prod"]
    }
}

So we can use different key values in different environments.





Through the above way, we can basically set the application title dynamically through gradle, apply icons, replace constants, set different package names, change channels and so on.

Finally, after all the configurations are completed, the normal packaging operation is performed.

As shown in the figure:


Picture Source Network

Picture Source Network



After packaging, you can see the generated apk package in the directory we specified.

Finally, put a project address:
https://github.com/Wing-Li/boon

OK.
If there are any problems in this article, please point out.
O(∩_∩)O

Posted by jrbilodeau on Wed, 03 Apr 2019 13:09:31 -0700