An overview,

From the last section on Gradle configuration and build optimization in Android, we have already understood the meaning of the various configuration items of Gradle, know how to optimize the build configuration, but only use others to provide good, not according to their own wishes to implement the function. In this chapter, we will briefly introduce Groovy, understand Gradle projects and tasks, introduce Gradle scripts, customize extensions according to the functions provided by Android Plugin, and write our own tasks and Gradle plug-ins. I believe you can have a further understanding of Gradle after watching it.

Introduction to Groovy syntax

Apache Groovy is a powerful, optionally typed and dynamic language, with static-typing and static compilation capabilities, for the Java platform aimed at improving developer productivity thanks to a concise, familiar and easy to learn syntax. It integrates smoothly with any Java program, and immediately delivers to your application powerful features, including scripting capabilities, Domain-Specific Language authoring, runtime and compile-time meta-programming and functional programming.

Apache Groovy is a powerful, optional type and dynamic language, with static typing and static compilation capabilities. It supports the Java platform and improves developer productivity due to its simplicity and ease of learning. It integrates smoothly with any Java program and provides powerful functionality for your applications, including scripting capabilities, domain-specific language authoring, runtime and compile-time metaprogramming, and functional programming. You can learn Groovy syntax in a special topic.

1. Compare to Java

  • Groovy is fully compatible with Java syntax, which means you can write Java code in Groovy, run it, and compile it into Java bytecode.
  • A semicolon at the end of a sentence is optional
  • Classes and methods are public by default
  • The compiler automatically adds getter/setter methods
  • Properties can be obtained using a dot
  • If the method returns a value, the value of the last expression is returned, omitting return.
  • == equals()
  • There is no NullPointerException

2. Groovy’s efficient features

  • The Assert statement can assert at any location
  • Weakly typed variable
  • The parentheses can be omitted if the calling method has arguments
  • Representation of strings
  • Collection class API, such as map, list of some methods
  • closure

3. Simple demonstration of Groovy syntax

// 1 Optional type definition def version = 1 // 2 assert Assert version == 2 // 3 parentheses are optional println version // 4 string def s1 ='Groovy'
def s2 = "version is ${version}"
def s3 = ' ''Three semicolons can break a line'' 'Println s1 // Groovy println s2 // version is 1 println s3 // println s1 // list def buildTools = ['ant'.'maven']
buildTools << 'gradle'Println buildtools.getClass () // class java.util.arrayList assert buildtools.size () == 3 // No exception // map def buildYears = ['ant': 2000,'maven':2004]
buildYears.gradle = 2009
println buildYears.ant  // 2000
println buildYears['gradle'] // class java.util.linkedhashmap def c1 = {v -> println v} def method1(Closure closure){ closure('param')
}
method1(c1) // param

 method1{ 
    c1 "hello"
}



Copy the code

Basic concepts in Gradle

1, the Project

Every build.gradle is a Project. Properties or methods defined directly in a module can be called using Project. If def valueTest = 5, you can use valueTest or project.valueTest to obtain the value of valueTest. The build.gradle root directory is also a Project in which ext{valueTest = 5} is defined, In the module of the build. Can be used directly in the gradle rootProject. ValueTest or rootProjext. Ext valueTest to reference. Ext is a global variable.

2, Task

A Project is composed of one or more tasks. Tasks have dependencies, which ensure the execution sequence of tasks. Well-known tasks include Clean, JAR, Assemble, etc.

Gradle build declaration cycle

Gradle’s declaration cycle is divided into three sections:

  • Initialization phase: read the include information in settings.gradle in the root Project, determine which projects to add to the build, and create Project instances, such asinclude ':app',':example'

  • Configuration phase: Execute build.gradle scripts of all projects, configure Project objects, create and configure tasks and related information. This stage executes commands in build.gradle, such as println written directly in Project
  • Run phase: Execute dependent tasks according to the task name passed by gradle command

Introduction of Gradle scripts

Gradle scripts can be used to reuse Gradle code and reduce maintenance costs. Other. gradle can be introduced by applying from: ‘other.gradle’. In daily development, we can optimize our projects by:

1. Extract configuration reuse items from the project

Gradle has many configuration items, such as version number, version name, minimum supported SDK version, whether to use obfuscations, dependent third-party libraries, etc. These configurations are likely to be reused in the project and may be omitted if they need to be modified. Extract these configurations and make them available to each build.gradle. 1. Create config.gradle in the root directory

ext {

    android = [
            compileSdkVersion: 27,
            minSdkVersion    : 16,
            targetSdkVersion : 27,
            versionCode      : 1,
            versionName      : "1.0.0",
            multiDexEnabled  : true
    ]

    version = [
            kotlin_version       : "1.2.50",
            support_version      : "27.1.1",
            constraint_version   : 1.1.3 "",
            junit_version        : "4.12",
            runner_version       : "1.0.2",
            espresso_core_version: "3.0.2"
    ]

    dependencies = [
            "androidJUnitRunner": "android.support.test.runner.AndroidJUnitRunner"."kotlin"            : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${version["kotlin_version"]}"."appcompat_v7"      : "com.android.support:appcompat-v7:${version["support_version"]}"."constraint_layout" : "com.android.support.constraint:constraint-layout:${version["constraint_version"]}"."junit"             : "junit:junit:${version["junit_version"]}"."runner"            : "com.android.support.test:runner:${version["runner_version"]}"."espresso_core"     : "com.android.support.test.espresso:espresso-core:${version["espresso_core_version"]}"]}Copy the code

Add apply from:”config.gradle” to build.gradle

android {
    compileSdkVersion rootProject.ext.android["compileSdkVersion"]
    defaultConfig {
        applicationId "net.loosash.learngradle"
        minSdkVersion rootProject.ext.android["minSdkVersion"]
        targetSdkVersion rootProject.ext.android["targetSdkVersion"]
        versionCode rootProject.ext.android["versionCode"]
        versionName rootProject.ext.android["versionName"]
        testInstrumentationRunner rootProject.ext.dependencies["androidJUnitRunner"]}... }... dependencies { implementation fileTree(dir:'libs', include: ['*.jar'])
    implementation rootProject.ext.dependencies["kotlin"]
    implementation rootProject.ext.dependencies["appcompat_v7"]
    implementation rootProject.ext.dependencies["constraint_layout"]
    testImplementation rootProject.ext.dependencies["junit"]
    androidTestImplementation rootProject.ext.dependencies["runner"]
    androidTestImplementation rootProject.ext.dependencies["espresso_core"]}Copy the code

2. Further extract the repeated parts in build.gradle

1. For dependent projects or componentized projects, it is recommended to extract the configuration from the dependent module. Create a new default.gradle file in the root directory.

apply plugin: 'com.android.library'
android {
    compileSdkVersion rootProject.ext.android["compileSdkVersion"]
    
    defaultConfig {
        minSdkVersion rootProject.ext.android["minSdkVersion"]
        targetSdkVersion rootProject.ext.android["targetSdkVersion"]
        versionCode rootProject.ext.android["versionCode"]
        versionName rootProject.ext.android["versionName"]
        testInstrumentationRunner rootProject.ext.dependencies["androidJUnitRunner"]
    }
    
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    // support
    implementation rootProject.ext.dependencies["appcompat_v7"] / /test
    testImplementation rootProject.ext.dependencies["junit"]
    androidTestImplementation rootProject.ext.dependencies["runner"]
    androidTestImplementation rootProject.ext.dependencies["espresso_core"]}Copy the code

2. Modify the build.gradle file in the module with the default configuration

apply from:".. /default.gradle"Android {// Writes the module-specific configuration resourcePrefix"example_"// Add a prefix to the resource name of the Module.Copy the code

3. Compare the build.gradle file before and after

For example, if multiple dependencies in the Support package are used in a project, changing the version number once can take effect for all projects, reducing maintenance costs.

5. Customize tasks

1. Define tasks

/gradlew hello prints hello world:

task hello{
	println 'hello world'
}
Copy the code

After creating a project in Android Studio, the clean task is created in the build.gradle root directory, which deletes the files in the Build folder

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

Type: SomeType Specifies the type of a Task created. Type tells Gradle which base Task the new Task will be derived from. For example, Gradle itself provides several generic tasks, the most common being the Copy Task. Copy is a class in Gradle. When we: task myTask(type:Copy), the task created is a Copy task. Similarly, it can be written as follows:

task copyDocs(type: Copy) {
    from 'src/main/doc'
    into 'build/target/doc'
}
Copy the code

2. Dependency

Gradle tasks have dependencies. During execution, dependent tasks are executed before target tasks.

task hello {
    doLast {
        println 'Hello world! '
    }
}
Task intro(dependsOn: hello) {
    doLast {
        println "I'm Gradle"}}Copy the code

The execution result

> Task :hello
Hello world!

> Task :intro
I'm Gradle

Copy the code

3. Group and describe

Tasks can add groups and descriptions

task hello {
    group 'Custom Group 1'
    description 'This is the Hello Task'
    doLast {
        println 'Hello world! '
    }
}
task intro(dependsOn: hello) {
    group 'Custom Group 2'
    description 'This is the intro Task'
    doLast {
        println "I'm Gradle"}}Copy the code

Enter. / gradlew tasks

Custom Group 1 tasks -------------------- hello - This is the Hello Task Custom Group 2 tasks -------------------- intro  - This is the intro TaskCopy the code

4. Obtain the defined tasks and add execution processing

Add execution processing to the existing Task Hello above.

task hello {
    group 'Custom Group 1'
    description 'This is the Hello Task'
    doLast {
        println 'Hello world! '
    }
}
task intro(dependsOn: hello) {
    group 'Custom Group 2'
    description 'This is the intro Task'
    doLast {
        println "I'm Gradle"
    }
}
tasks.hello{
    doFirst{
        println "prepare to say"}}Copy the code

Execute./gradlew intro to get the following output

> Task :hello
prepare to say
Hello world!

> Task :intro
I'm Gradle

Copy the code

Sixth, custom build function

1, buildTypes

You can use the properties here to change the package name for the Debug and release versions. Use the following: 1) Modify applicationId to enable both the Debug and Release versions to be installed

android { ... BuildTypes {debug {// bebug version package name is XXX.XXX.xxx.debug applicationIdSuffix".debug"}... }}Copy the code

2) Sign the release version

  signingConfigs {
        release {
            keyAlias 'xxxx'
            keyPassword 'xxxxxx'
            storeFile file('your-keystore-path')
            storePassword 'xxxxxx'
        }
    }

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

        }
    }
Copy the code

2, productFlavors

Different versions of APK can be generated based on this property, which can carry different features. The most common is multi-channel packaging, and different APKS can be generated according to different environments.

ProductFlavors {productA{// Define a specific version number // applicationIdSuffix".a"
			applicationId "xxx.xxx.xxx.a"// define a specific versionName versionName"Version - a - 1.0"// define specific BuildConfig buildConfigField("String"."CUSTUMER_CONFIG"."xaxaxaxa")
		}
		productB{
			applicationId "xxx.xxx.xxx.b"
			versionName "Version - b - 1.0"
			buildConfigField("String"."CUSTUMER_CONFIG"."xbxbxbxb") } } dependencies{ ... // Specific versions depend on productACompile'the IO. Reactivex. Rxjava2: rxjava: 2.0.1'. }Copy the code

3. Add custom processing

Copy the generated APK to the custom folder with the custom name

    applicationVariants.all { variant ->
        tasks.all {
            if ("assemble${variant.name.capitalize()}".equalsIgnoreCase(it.name)) {
                it.doLast {
                    copy {
                        rename { String fileName ->
                            println "-- -- -- -- -- -- -- -- -- -- -- --${fileName}-- -- -- -- -- -- -- -- -- -- -- -- -- --"
                            fileName.replace(".apk"."-${defaultConfig.versionCode}.apk")
                        }
                        def destPath = file("/Users/solie_h/Desktop/abc/")
                        from variant.outputs.first().outputFile
                        into destPath
                    }
                }
            }
        }
    }
Copy the code

Seven,

We can operate Gradle to fulfill some of our own needs, but we still need some work to provide Gradle plugins. Next, we will continue to read the source code of Android Plugin, and also find some open source projects with Gradle plugins to learn. Replugin, Tinker, to further improve my knowledge of Gradle.

Pay attention to wechat public number, the latest technology dry goods real-time push