The use of gradle in actual projects

foreword

As a practical series, this article mainly explains the use of gradle in actual projects, and the principle part will not explain too much. This article will use some knowledge of Groovy. If you do not understand the basic syntax of Groovy, you can refer to the chairman's article Groovy Basics , Gradle Execution Timing . Of course, I will also annotate the places used, even if you don't read it, that's fine.

1. Unified dependency management

Here mainly refers to the compiled version of the project, the version of the dependent library, etc. Here, refer to the official Google project, and build a separate version.gradle file in the root directory of the project to manage the version uniformly. The benefits of this are obvious, especially for multi-module projects. , it is very convenient to do unified version management to avoid conflicts. But doing this will also bring a disadvantage, that is, when you inspect the new version, when there is a new version, you can't check it. But overall, the pros outweigh the cons.

Let's take a look at what's in the version.gradle file

 // version.gradle文件
ext{
android = [
        compileSdkVersion: 27,
        minSdkVersion    : 21,
        targetSdkVersion : 27,
        versionName: "1.0.0"
    ]
dependencies = [
        appcompatV7 : 'com.android.support:appcompat-v7:27.1.1',
        design      : 'com.android.support:design:27.1.1',
        constrant :'com.android.support.constraint:constraint-layout:1.0.2'
    ]
}

As an illustration here, only a few basic dependencies are written, and the next step is to use these configurations. First introduce in the project's build.gradle file

buildscript {
    apply from: 'version.gradle'
    repositories {
        google()
        jcenter()
    }
    …………
}

Then, it can be used happily in the gradle of the module.

compileSdkVersion rootProject.ext.android.compileSdkVersion
defaultConfig {
    minSdkVersion rootProject.ext.android.minSdkVersion
    targetSdkVersion rootProject.ext.android.targetSdkVersion
    versionCode getCode()
    versionName rootProject.ext.android.versionName
    …………
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation rootProject.ext.dependencies.appcompatV7
    implementation rootProject.ext.dependencies.constrant
    implementation rootProject.ext.dependencies.design
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'

}

2. Automatically generate versionCode according to versionName

Careful friends may have found that the versionCode above does not use the versionCode configured in version.gradle. In fact, versionCode is not configured in version.gradle. Here I use the versionCode automatically generated by versionName according to the product requirements.

// 读取version.gradle里的versionName 生成versionCode
def getCode() {
    String name = rootProject.ext.android.versionName
    List arr = name.tokenize('.')
    int code1
    int code2
    int code3
    code1 = arr.get(0).toInteger() * 10000
    code2 = arr.get(1).toInteger() * 100
    code3 = arr.get(2).toInteger()
    int code = code1 + code2 + code3
}

The above is the method of obtaining the version number. It adopts a three-segment format, with two digits in each segment, which can be changed according to actual needs. In addition, in the Groovy grammar, if you do not write a return statement, the code calculated in the last line will be automatically returned. In this way, there is no need to modify versionName and versionCode every time the version is upgraded. Note that the above code is in the android sibling field.

3. Elegant signature settings

How to generate the signature file will not be discussed here. It should be noted that for the convenience of reference, I put the jks signature file in the app directory. Also in this directory, I created a signing.properties file. The content of the file is as follows:

// signing.properties 文件
storePass=123456
alias=key
keyPass=123456
v2SigningEnabled=false
signingFile=key.jks

It's the basic signature information. Let's take a look at the use in the gradle of the module:

signingConfigs {
    // 定义signConfig并赋值
    signConfig
    File propFile = file('signing.properties')
    if (propFile.exists()) {
        Properties props = new Properties()
        props.load(new FileInputStream(propFile))
        if (props.containsKey('signingFile') && props.containsKey('storePass') &&
                props.containsKey('alias') && props.containsKey('keyPass') && props.containsKey("v2SigningEnabled")) {
            signConfig.storeFile = file(props['signingFile'])
            signConfig.storePassword = props['storePass']
            signConfig.keyAlias = props['alias']
            signConfig.keyPassword = props['keyPass']
            android.signingConfigs.signConfig.v2SigningEnabled = props['v2SigningEnabled']
        } else {
            android.buildTypes.release.signingConfig = null
            android.buildTypes.flavor.signingConfig = null
        }
    } else {
        android.buildTypes.release.signingConfig = null
        android.buildTypes.flavor.signingConfig = null
    }
    print("signingConfigs===========${signConfig}")
}

It is to define a signConfig and assign a value, very simple. But writing in this way avoids exposing signature information directly in gradle. Of course, you can run the gradle guild command in the terminal to verify and see if the print result is correct.

After configuration, you can directly reference it in buildTypes.

release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.signConfig
        …………
    }

4. Customize BuildConfig

In actual development, the server generally has a formal environment and a generation environment, and sometimes there is a back-end developer's own local server environment. At this time, different environments are configured by customizing the buildConfigField.

release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.signConfig
        buildConfigField "boolean","isRelease","true"
        …………
    }

We set the value to true in the type of release, and set the other types to fasle. In this way, after synchronization, there will be an additional static variable isRelease in the BuildConfig class, so that in the java code, we can judge based on this variable Whether it is a test environment or a production environment, you can also control whether log output is required according to this variable. Even you can directly configure the test server address and production server address into it.

buildConfigField 'String','API_SERVER_URL','"http://lxy.v1/"'

5. Different versions configure different package names and AppNames

In actual development, in order to distinguish the beta version from the official version, it is sometimes necessary to set the package name of the beta version and the app name to be different. In fact, such a requirement can be achieved in minutes with gradle

release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.signConfig
        buildConfigField "boolean","isFlavor","false"
        manifestPlaceholders = [
                APP_NAME           : "release"
        ]
    }

    debug {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.signConfig
        buildConfigField "boolean","isFlavor","false"
        applicationIdSuffix 'debug'
        manifestPlaceholders = [
                APP_NAME           : "debug"
        ]
    }

As above, in the debug version, the applicationIdSuffix 'debug' is configured. If it is packaged in this way, debug will be added after the original package name, so that it will be two different apps installed on the mobile phone, because the package names are different. Such a test machine can test the test package and the official package at the same time. Moreover, the manifestPlaceholders placeholder is also set, so that it can be directly referenced in the menifests file, and different package names and app names can be realized.

// 清单文件里引用
android:label="${APP_NAME}"

There is a pit here. If this configuration is done, if you use the sdk with WeChat payment, sharing, etc. in your project, pay attention to the package where the class of the callback after payment and sharing is separated from the official version, otherwise the callback will not be received (WeChat). The sdk is sometimes very painful).

6. Customize the file name of the packaged apk

Add the following code in the field of android

applicationVariants.all { variant ->
    variant.outputs.all { output ->
        def newApkName
        newApkName = getTime() + defaultConfig.versionName + "-" + defaultConfig.versionCode + ".apk"
        outputFileName = newApkName
    }
}

The getTime() method is at the same level in the android domain

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

7, java8 and dataBinding support

Java8's lambda is still very concise, and similarly, dataBinding is just as cool to use

// 当然在android领域里设置了
 
 dataBinding {
    enabled = true
}

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

8. Customize the jinlibs directory

Now the new as project will automatically generate a libs directory in the app directory, you can set the file directory of jar, so library, etc. in gradle

sourceSets {
    main {
        jniLibs.srcDirs = ['libs']
    }
}

9. Other configurations such as obfuscation, multi-channel packaging, etc.

If the configuration is obfuscated, configure it in the release version, be careful not to forget to add the obfuscated file

minifyEnabled true
zipAlignEnabled true
shrinkResources true

It is slow to use gradle to package multi-channel, so I won't talk about it here. Personally, I feel that 360 multi-channel packaging is very useful. I recommend it.

The above are the commonly used configurations in actual projects. Of course, there are many less commonly used configurations, such as packaging aar to maven, gradle compilation, gradle performance detection, gradle acceleration, using gradle caching, custom gradle plugins, etc., space problems , everyone learns by themselves...

Attach the complete demo address , if it is helpful to you, please start to encourage it!

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326691016&siteId=291194637