Variety of use environment

In the development process, we may encounter such a situation: replacing part of UI or adding or deleting a certain function module on the basis of the existing APP. Generally speaking, the two APKs have a lot in common. The solutions to this situation are as follows:

1. Copy and paste the existing code and modify it in another project

2. Use the branching capabilities of code hosting tools

3. Use a global static variable to control a function switch

The above method is fine when the number of newly packaged APKs is small, but when too many APKs need to be packaged, the above method will become extremely troublesome in the subsequent development. Therefore, in this case, the advantages of convenient management of the variation are fully demonstrated

Formal use

Gradle configuration

First of all, you need to configure flavorDimensions in the Gradle of the Module where the app is located so that the flavors of the variants can be kept in the same dimension

The value of flavorDimensions is freely definable

Create productFlavors on Android, create a suboption with a custom name, and create a folder named “SRC” that is the same as the name of the suboption in productFlavors

After the configuration is complete, click sync to update the configuration and the first step of using the variant is complete

Click on the Build Variants TAB in the lower left corner of Android Studio to expand the Variants catalog and select the Variants for which the files will be compiled at runtime

Of course, just by doing this, our packaged APK packages will overwrite each other because the compiled APK packages have the same names.

We can add configurations of the variants under productFlavors and set the applicationId separately, so that the compiled APK can coexist on the phone.

productFlavors{
        variant1{
            applicationId "com.sariki.variants1"
        }
        variant2{
            applicationId "com.sariki.variants2"}}Copy the code

Function Module Configuration

After the completion of the variant construction, then it is time to code, there are two points to pay attention to: 1. The files in the variant inherit the folder structure in the main directory, so the package structure of the new variant directory must be the same as that of main

2. A file with the same name as that in main cannot exist. You should delete the file from main and create it in the directory of variants (for example: In the main directory, you need to jump to activityB, but this activityB has different functions according to the mutation. In this case, you need to create the activityB in the different mutation and delete the corresponding activityB file in the main directory.)

Since packaging more app, we may according to the variation in some interface to display hide some components for operation, if largely consistent with the ontology and no new features, we can not take in variant after deleting the main directory file created in the way to deal with, can use BuildConfig class to determine. BuildConfigField = productFlavors = Gradle

productFlavors{
        variant1{
            applicationId "com.sariki.variants1"
            buildConfigField("boolean"."SHOW_TOAST"."true")
        }
        variant2{
            applicationId "com.sariki.variants2"
            buildConfigField("boolean"."SHOW_TOAST"."false")}}Copy the code

This variable can be used in code for judgment processing

BuildConfigField creates a static member variable based on your parameters at compile time, so we can also use this to configure network ports to suit the server variants

productFlavors{
        variant1{
            applicationId "com.sariki.variants1"
            buildConfigField("boolean"."SHOW_TOAST"."true")
            buildConfigField("String"."SERVER_HOST"."\" http://111.111.11.1/\ "")
        }
        variant2{
            applicationId "com.sariki.variants2"
            buildConfigField("String"."SHOW_TOAST"."true")
            buildConfigField("String"."SERVER_HOST"."\" http://222.222.22.2/\ "")}}Copy the code

Using buildConfigField makes the Gradle file too cluttered when too many variables need to be manipulated. In this case, we can create a new Java file as a carrier. Create static member variables there instead of BuildConfig (this file also needs to be created separately in the same hierarchical directory of the different variant folders)

Resource File Configuration

Same as the Java files, resource files if there are changes according to the variant, also need to the corresponding file directory to create the same file, but in a resource file and there is a difference: Java file variants with the level of the same name with the main resource file directory of the same rank resource file with the same coexist, compile selection variant files in the directory.

Here’s an example:

We set the icon in the application. Take the background_test file as an example. We create the file in the main and variant resource file directories respectively

The background_test file in the main directory is blue.

Background_test in the variant1 directory is grey.

Background_test in the variant2 directory is black.

The final compiled APK shows the color of the background_test variant. As a result, any other image resource file that needs to be modified for variants can be modified in this way.

With image files in mind, we come back to the point where we select files in the directory of mutant files at compile time. In the values folder, we usually define the configuration using strings. XML, colors. XML, etc. These XML files usually contain a lot of item configuration, If the operation of the above picture to copy a complete XML file to the variant directory will cause a waste of resources and expand the project space, but if you do not copy the XML file and how to modify part of the file configuration? We can rely on XML configuration overrides to solve this problem.

Here’s an example:

Create a new string named “test_txt” in strings. XML in the main directory. After compiling, it needs to be displayed as “variation 1” in Variant1 and “variation 2” in Variant2. So, we can simply create a new XML file (optionally named) in the corresponding variant directory, and then create a string with the same name of “test_txt” and assign it. In this way, the string in the compiled variant of XML overwrites the value of the strings. XML string of the same name in the main directory.

Strings. XML configuration in the main directory

Configure variant_stings. XML in the variant1 directory

The variant_stings. XML configuration in the variant2 directory

Test the XML of the interface

<? xml version="1.0" encoding="utf-8"? ><androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:textSize="30sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:text="@string/tip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <TextView
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:textSize="30sp"
        android:text="@string/test_txt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code

So once we’ve set the content for our TextView we can compile it and see what it looks like

variants1:

variant2:

In this way, we can achieve the goal of changing text or color with only a few configuration changes

The key configuration

In the development process, we may use third-party SDK to support us to do some functions, and these SDKS will use unique keys to obtain access, and these SDKS also need to be used when we build variants. So how should we configure these keys?

Most SDK keys now need to be configured in AndroidManifest. In AndroidManifest, we can reference gradle configuration directly.

Take the key configuration of netease cloud as an example:

<meta-data
            android:name="com.netease.nim.appKey"
            android:value="" />
Copy the code

We need to fill in the value column with the key obtained from netease yunxin. In this case, we can use reference to configure the key.

First, go to productFlavors under Gradle and configure the Manifestplaceholder parameters for the variant

productFlavors{
        variant1{
            applicationId "com.sariki.variants1"
            manifestPlaceholders = [valueName : "xxxxxxxxxx"]
        }
        variant2{
            applicationId "com.sariki.variants2"
            manifestPlaceholders = [valueName : "xxxxxxxxxxxx"]}}Copy the code

Format: [custom variable name: “key value “]

Then go back to the manifest file and use ${XXX} to reference the specified value

<meta-data
            android:name="com.netease.nim.appKey"
            android:value="${valueName}" />
Copy the code

It is also possible to use ${applicationId} to reference package names in configurations that require a complete package name structure, such as some vendor push and other SDK configurations

<permission
        android:name="${applicationId}.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
Copy the code

However, in some cases, the package name cannot be directly referenced. For example, we need to manually create WXEntryActivity and declare it in the manifest file when accessing the wechat SDK used for third-party login.

<activity android:name=".wxapi.WXEntryActivity"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.Translucent.NoTitleBar"
            android:exported="true"
            android:launchMode="singleTask"
            />
Copy the code

However, since the package name used when we build the variant is different, the name field index is not in the location of WXEntryActivity. If you configure the WXEntryActivity directly, you will find that WXEntryActivity cannot be invoked. In this case, you need to use an alias to configure the WXEntryActivity

<activity-alias
            android:name="${applicationId}.wxapi.WXEntryActivity"
            android:exported="true"
            android:targetActivity=".wxapi.WXEntryActivity" />
Copy the code

Add these configurations to index properly

Multi-module variant configuration

In some development environments, we may need to configure multiple modules in a variety of configurations, which is not much different from the above configuration process, but there are two points to note: 1. FlavorDimensions of multiple modules need to be consistent 2. ProductFlavors of multiple modules should be consistent. That is, how many productFlavors can be configured in Module1 and how many productFlavors can be configured in Module2. In this way, there are too many places to configure a new package and the configuration process becomes tedious, which is against the original intention.

At the end

That is variant will be involved in the process of building some of the ways, but it is worth mentioning that variant has ma3 jia3 difference and channel package, channel package is generally to statistics on different application market flow and related statistical channel is set in the APP, and waistcoat bag besides can satisfy the demand of channel package, changes can be made according to different varieties resources and function change, More used in a source code according to the needs of different APP customization scenarios.