Gradle's Seller Show and Buyer Show

Posted by Kurt on Sun, 07 Jul 2019 21:59:54 +0200

Most people have experienced high school, it is not difficult to find that 650 points in the college entrance examination and 450 points in the book list are basically the same, why?

This is often not because they are exposed to more information, but because they deal with information in a different way. They are often good at collating information and acquiring a "systematic knowledge system".

Written in front

Today, I flipped through my previous blog and found that the last one was published in 2017-01-12. Unconsciously, three months passed, and there seemed to be no systematic summary between them. Think about it carefully. It's still a bit square.

Three months said it was not long, but it was enough for a lot of things to happen. For example, I went live.( YOLO ) Industry, starting to take over predecessors( Piasy,promeG There is no need to say more about the twists and turns between projects. Fortunately, it has adapted to the current rhythm, and blogs will be updated slowly.

Actually, live broadcasting is not as complicated as imagined. Generally speaking, it is just the collection terminal (push terminal) and the audience terminal (pull terminal). Then add some gift system, bullet screen system, chat system, face recognition, game interaction, Lianmai interaction, beauty and other functions to make it full.

As for live broadcasting, we will not discuss it in depth for the time being. Today let's talk about Gradle. Maybe a lot of people will have this feeling, why do I see so many "Gradle from entry to proficiency" and still can not manage the construction of their own projects? Today, we're going to sort out Gradle's related knowledge points, which may be useful.

Gradle Mind Map

In order to sort out Gradle's knowledge points conveniently, I drew the following mind map, and I hope you can point out the shortcomings. In fact, each of these points can be expanded by writing a separate blog, and many of them have been summarized very well, so I won't dwell on them here.

It is also necessary to find out why we should use Gradle and what Gradle can do. Officially, Gradle uses understandable DSL grammar to make the compilation, construction, testing, packaging and deployment required in the development process very simple and easy to reuse. Gradle can simplify our development from all aspects of the development process.

Gradle Actual Warfare

As the saying goes: Talk is cheap, show me the code. Next, I would like to share how our Gradle is configured.

The build.gradle file in the project root directory

// Top-level build file where you can add configuration options common to all sub-projects/modules.

apply from: 'buildsystem/dependencies.gradle'

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        apply from: 'buildsystem/dependencies.gradle'

        classpath "com.android.tools.build:gradle:$gradleAndroidVersion"

        classpath 'com.github.promeg:android-multi-channel-plugin:0.1'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

build.gradle file under app

import java.text.SimpleDateFormat

import static org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS
import static org.apache.tools.ant.taskdefs.condition.Os.isFamily

if (!testDevelopURC) {
    apply plugin: 'com.android.library'
} else {
    apply plugin: 'com.android.application'
}
apply plugin: 'android-multi-channel'

def keyConfigPath
if (isFamily(FAMILY_WINDOWS)) {
    keyConfigPath = System.getenv('USERPROFILE') + File.separator + ".ssh" + File.separator +
            "androidKeystore.properties"
} else {
    keyConfigPath = System.getenv('HOME') + "/.ssh/androidKeystore.properties"
}
Properties props = new Properties()
if (new File(keyConfigPath).exists()) {
    props.load(new FileInputStream(file(keyConfigPath)))
}

android {
    def rootDep = rootProject.ext

    compileSdkVersion rootDep.androidCompileSdkVersion
    buildToolsVersion rootDep.androidBuildToolsVersion
    defaultConfig {
        minSdkVersion rootDep.androidMinSdkVersionn
        targetSdkVersion rootDep.androidTargetSdkVersion
        versionCode rootDep.releaseVersionCode
        versionName rootDep.releaseVersionName

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        multiDexEnabled true
        vectorDrawables.useSupportLibrary = true

        if (!testDevelopURC) {
            applicationId "tv.yoloyolo.renlei.gradlepractice"
        } else {
            consumerProguardFiles 'proguard-rules.pro', 'proguard-fresco.pro'
        }

        buildConfigField "String", "API_BASE_URL", "\"https://api.test.base\""

        manifestPlaceholders = [EASEMOB_APPKEY: "publish#publish"]
    }

    lintOptions {
        abortOnError false
    }

    packagingOptions {
        exclude 'META-INF/LICENSE.txt'
        exclude 'LICENSE.txt'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/NOTICE.txt'
        exclude 'META-INF/services/javax.annotation.processing.Processor'
        exclude 'META-INF/rxjava.properties'
    }

    signingConfigs {
        release {
            storeFile file(props['keystore'])
            storePassword props['keystore.password']
            keyAlias "promegu"
            keyPassword props['key.password']
        }
    }

    dexOptions {
        maxProcessCount 8
        javaMaxHeapSize "6g"
    }

    testOptions.unitTests.all {
        testLogging {
            events 'passed', 'skipped', 'failed', 'standardOut', 'standardError'
            outputs.upToDateWhen { false }
            showStandardStreams = true
        }
        // configure the test JVM arguments
        jvmArgs '-noverify'
    }

    aaptOptions {
        cruncherEnabled false
    }

    publishNonDefault true

    productFlavors {
        dev {
            ndk {
                abiFilter "armeabi"
            }

            buildConfigField "String", "API_BASE_URL", "\"https://api.test.base\""

            manifestPlaceholders = [EASEMOB_APPKEY: "test#test"]
        }

        production {
            ndk {
                abiFilter "armeabi"
            }
            // inherit from default config
        }
    }

    buildTypes {
        debug {
            minifyEnabled false
            debuggable true
            ext.enableCrashlytics = false
            signingConfig signingConfigs.release
        }

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

multiFlavors {
    prefix = "TEST_";
    def released = new SimpleDateFormat('yyyy-MM-dd').format(new Date())
    subfix = "_$released";
    defaultSigningConfig = android.signingConfigs.release
    channelConfig {
        production {
            childFlavors =
                    ["Tencent", "360", "Baidu", "Alibaba", "GFan", "Sogou",
                     "Lenovo", "XiaoMi", "Meizu", "OPPO", "Huawei", "GooglePlay"]
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])

    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })

    compile 'com.android.support:appcompat-v7:25.3.1'

    testCompile 'junit:junit:4.12'

    testCompile 'org.robolectric:shadows-support-v4:' + rootProject.ext.robolectricVersion

    compile('com.github.promeg:android-multi-channel-plugin-lib:0.1') {
        exclude module: 'appcompat-v7'
    }
}

External dependencies.gradle file

I created a folder called build system in the project root directory, under which the dependencies.gradle file was placed.

allprojects {
    repositories {
        jcenter()
    }
}

ext {
    androidBuildToolsVersion = '25.0.2'
    androidCompileSdkVersion = 25
    androidMinSdkVersionn = 15
    androidTargetSdkVersion = 25
    androidSupportSdkVersion = '25.1.0'

    gradleAndroidVersion = '2.2.3'

    releaseVersionCode = 1
    releaseVersionName = "1.0"

    robolectricVersion = '3.0'
}

The gradle.properties file in the project root directory

# Project-wide Gradle settings.

# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.

# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html

# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
#org.gradle.jvmargs=-Xmx1536m

# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

org.gradle.jvmargs=-Xmx8192m -XX\:MaxPermSize\=3072m
org.gradle.daemon=true
org.gradle.configureondemand=true
org.gradle.parallel=true

testDevelopURC=false

Applied knowledge points

Basically, the configuration of these files is simplified from the YOLO project, but does not affect the research. Next, let's look at the points used. Because the implementation of each point on the Internet has been introduced a lot of very comprehensive, here I just say about the point, specific you can own google. I will also attach some links for your reference.

Depending on external configuration

If our configuration file is too large or needs to be reused, we can consider extracting it into a separate file. The introduction method is as follows:

apply from: 'buildsystem/dependencies.gradle'

Global configuration

Stack Overflow: How to define common android properties for all modules using gradle

ext {
    androidBuildToolsVersion = '25.0.2'
    androidCompileSdkVersion = 25
    androidMinSdkVersionn = 15
    androidTargetSdkVersion = 25
    androidSupportSdkVersion = '25.1.0'

    gradleAndroidVersion = '2.2.3'

    releaseVersionCode = 1
    releaseVersionName = "1.0"

    robolectricVersion = '3.0'
}

placeholder

If we define placeholders in manifest, we can assign them in module build.gradle.

<meta-data
            android:name="EASEMOB_APPKEY"
            android:value="${EASEMOB_APPKEY}" />

manifestPlaceholders = [EASEMOB_APPKEY: "publish#publish"]

ProductFlavors

For Product Flavors, we can do it in Build - > Select Build Variant... Select the default configuration we use in development.

productFlavors {
    dev {
        ndk {
            abiFilter "armeabi"
        }

        buildConfigField "String", "API_BASE_URL", "\"https://api.test.base\""

        manifestPlaceholders = [EASEMOB_APPKEY: "test#test"]
    }

    production {
        ndk {
            abiFilter "armeabi"
        }
        // inherit from default config
    }
}

BuildTypes

Gradle official documents: BuildType This article is about what you can configure with BuildType.

debug {
            minifyEnabled false
            debuggable true
            ext.enableCrashlytics = false
            signingConfig signingConfigs.release
        }

BuildConfig

Custom BUILDCONFIG

buildConfigField "String", "API_BASE_URL", "\"https://api.test.base\""

dependencies

It includes several types of dependency and how to resolve dependency conflicts.

Gradle official documents: Basic knowledge of dependency management

Android Official Documents: Add Build Dependencies

Exclusion of transitive dependencies

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])

    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })

    compile 'com.android.support:appcompat-v7:25.3.1'

    testCompile 'junit:junit:4.12'

    testCompile 'org.robolectric:shadows-support-v4:' + rootProject.ext.robolectricVersion

    compile('com.github.promeg:android-multi-channel-plugin-lib:0.1') {
        exclude module: 'appcompat-v7'
    }
}

autograph

Some of Groovy's grammars are used here, mainly to find your own Android Keystore. properties file and read the configuration.

Android Official Documents: Sign your application

def keyConfigPath
if (isFamily(FAMILY_WINDOWS)) {
    keyConfigPath = System.getenv('USERPROFILE') + File.separator + ".ssh" + File.separator +
            "androidKeystore.properties"
} else {
    keyConfigPath = System.getenv('HOME') + "/.ssh/androidKeystore.properties"
}
Properties props = new Properties()
if (new File(keyConfigPath).exists()) {
    props.load(new FileInputStream(file(keyConfigPath)))
}

signingConfigs {
        release {
            storeFile file(props['keystore'])
            storePassword props['keystore.password']
            keyAlias "promegu"
            keyPassword props['key.password']
        }
}

Various options configurations

Gradle official documents: Android Plugin DSL Reference

packagingOptions {
    exclude 'META-INF/LICENSE.txt'
    exclude 'LICENSE.txt'
    exclude 'META-INF/LICENSE'
    exclude 'META-INF/NOTICE'
    exclude 'META-INF/NOTICE.txt'
    exclude 'META-INF/services/javax.annotation.processing.Processor'
    exclude 'META-INF/rxjava.properties'
}
dexOptions {
        maxProcessCount 8
        javaMaxHeapSize "6g"
    }

    testOptions.unitTests.all {
        testLogging {
            events 'passed', 'skipped', 'failed', 'standardOut', 'standardError'
            outputs.upToDateWhen { false }
            showStandardStreams = true
        }
        // configure the test JVM arguments
        jvmArgs '-noverify'
    }

    aaptOptions {
        cruncherEnabled false
    }

Multi-channel Packaging

This multi-channel packaging is actually a Gradle plug-in written by my colleagues before. Portal.

Of course, if you are familiar with Groovy grammar, you can write it yourself. Writing Gradle plug-in.

multiFlavors {
    prefix = "TEST_";
    def released = new SimpleDateFormat('yyyy-MM-dd').format(new Date())
    subfix = "_$released";
    defaultSigningConfig = android.signingConfigs.release
    channelConfig {
        production {
            childFlavors =
                    ["Tencent", "360", "Baidu", "Alibaba", "GFan", "Sogou",
                     "Lenovo", "XiaoMi", "Meizu", "OPPO", "Huawei", "GooglePlay"]
        }
    }
}

Accelerated compilation

Android Official Documents: Optimize your compilation speed

dexOptions {
        maxProcessCount 8
        javaMaxHeapSize "6g"
    }

org.gradle.jvmargs=-Xmx8192m -XX\:MaxPermSize\=3072m
org.gradle.daemon=true
org.gradle.configureondemand=true
org.gradle.parallel=true

Other

In addition to the points mentioned above, there are some good links to summarize. In fact, many blogs in China are similar, referring to official documents, so it is recommended to read the official documents directly.

epilogue

Maybe there are a lot of things mentioned above, but you can check them on demand. And if you want to do good, you must first sharpen your tools. For us, learning Gradle well can undoubtedly be of great help to development and save more time.

Thank you for the detailed tutorial of official documents, and also for those friends who like to share. If the link above is infringed, please let me know, I will delete it.

Topics: Gradle Android ssh Junit