Android中的Gradle之玩转自定义功能

1、概述

经过上一节Android中的Gradle之配置及构建优化,咱们已经了解了Gradle的各个配置项的含义,知道了如何优化构建配置,但只会用别人提供好的,没法按本身的意愿实现功能。经过本章节,咱们将简单介绍Groovy,了解Gradle中的Project与Task,引入gradle脚本,根据android plugin插件提供的功能自定义扩展,以及写本身的Task及本身的gradle插件,相信看完以后能对gradle有进一步的了解。java

2、Groovy语法简介

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.react

上面的意思大体以下:
Apache Groovy是一种功能强大,可选类型和动态语言,与静态类型和静态编译功能,支持Java平台,因为其简洁与易学性提升了开发人员的开发效率。它能够与任何Java程序平滑集成,并为您的应用程序提供强大的功能,包括脚本功能,域特定语言创做,运行时和编译时元编程以及函数编程。
具体能够专题进行学习Groovy语法。android

一、与java比较

  • Groovy彻底兼容java的语法,也就是说在Groovy能够编写java代码并运行,最终编译成java字节码。
  • 句末的分号是可选的
  • 类、方法默认是public的
  • 编译器自动添加getter/setter方法
  • 属性可使用点号获取
  • 方法若是有返回值,最后一个表达式的值即做为返回,省略return。
  • ==等同于equals()
  • 没有NullPointerException

二、Groovy高效特性

  • assert语句能够在任何位置断言
  • 弱类型变量
  • 调用方法若是有参数,能够省略括号
  • 字符串的表示
  • 集合类api,如map、list的某些方法
  • 闭包

三、Groovy语法简单演示

// 1 可选的类型定义
def version = 1

// 2 assert
assert version == 2

// 3 括号是可选的
println version

// 4 字符串
def s1 = 'Groovy'
def s2 = "version is ${version}"
def s3 = '''三个 分号 能够 换行 '''
println s1 // Groovy
println s2 // version is 1
println s3   // 三个
                // 分号
                // 能够
                // 换行

// 5 集合api
// list
def buildTools = ['ant','maven']
buildTools << 'gradle'
println buildTools.getClass() // class java.util.ArrayList
assert buildTools.size() == 3 // 没有异常
// map
def buildYears = ['ant':2000,'maven':2004]
buildYears.gradle = 2009
println buildYears.ant  // 2000
println buildYears['gradle'] // 2009
println buildYears.getClass() // class java.util.LinkedHashMap

// 6 闭包
def c1 = {
    v ->
            println v
}
def method1(Closure closure){
    closure('param')
}
method1(c1) // param

 method1{ 
    c1 "hello"
}



复制代码

3、Gradle中的基础概念

一、Project

能够说每个build.gradle都是一个Project对应项目中就是某一个module,直接在其中定义的属性或者方法均可以使用project来调用。如def valueTest = 5可使用valueTest 或者project.valueTest来获取valueTest的值。
根目录的build.gradle也是一个Project,在其中定义ext{valueTest = 5},在module中的build.gradle中能够直接使用rootProject.valueTest或者rootProjext.ext.valueTest来引用。这里的ext是一个全局变量的意思。
编程

二、Task

Project又是由一个或者多个Task组成,Task存在着依赖关系,依赖关系又保证了任务的执行顺序。好比咱们熟知的Task有clean,jar,assemble等。
api

三、Gradle构建声明周期

Gradle的声明周期分为三段:
bash

  • 初始化阶段:读取根工程中 settings.gradle 中的 include 信息,决定有哪几个工程加入构建,建立Project实例,如 include ':app',':example'
  • 配置阶段:执行全部工程的 build.gradle 脚本,配置Project对象,建立、配置Task及相关信息。该阶段会执行build.gradle中的命令,如直接写在Project中的println命令
  • 运行阶段:根据gradle命令传递过来的task名称,执行相关依赖任务

4、Gradle脚本的引入

引入gradle脚本,能够实现对gradle代码的复用,减小维护成本。能够经过apply from: 'other.gradle'来对other.gradle进行引入。平常开发中,咱们能够经过以下几点优化咱们的项目:
微信

一、将项目中的配置复用项提取出来

在gradle中有不少配置项,如版本号,版本名称,最小支持SDK版本,是否使用混淆,依赖的第三方库等。这些配置颇有可能在项目中复用,如需修改,可能会致使遗漏,将这些配置提取出来,供各个build.gradle使用。具体以下:
一、在根目录新建config.gradle
闭包

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"]}"
    ]
    
}
复制代码

二、在根目录build.gradle中增长apply from:"config.gradle"
三、修改module的gradle文件
app

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"]
}
复制代码

二、对于build.gradle中重复部分进一步提取

一、对于依赖工程或者组件化工程,建议将依赖module中的配置也提取出来。在根目录新建default.gradle文件。
maven

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"]
}

复制代码

二、修改使用默认配置的module中的build.gradle文件

apply from:"../default.gradle"

android{
    // 写入该模块特定的配置
    resourcePrefix "example_" //给 Module 内的资源名增长前缀, 避免资源名冲突

}

dependencies {
    // 该模块使用的依赖

}

复制代码

三、对build.gradle文件先后作一个对比

修改后的好处在于,好比在工程中多处使用support包中的依赖,一次修改版本号便可对所有工程生效,下降了维护成本。

修改前
修改后
修改前
修改后
修改前
修改后

5、自定义Task任务

一、定义任务

最经常使用的写法,执行./gradlew hello打印出hello world

task hello{
	println 'hello world'
}
复制代码

在android studio建立项目后,会在根目录的build.gradle中建立clean任务,就是删除build文件夹下文件

task clean(type: Delete) {
    delete rootProject.buildDir
}
复制代码

说明:Task建立的时候能够经过 type: SomeType 指定Type,Type其实就是告诉Gradle,这个新建的Task对象会从哪一个基类Task派生。好比,Gradle自己提供了一些通用的Task,最多见的有Copy 任务。Copy是Gradle中的一个类。当咱们:task myTask(type:Copy)的时候,建立的Task就是一个Copy Task。相似的,有以下写法:

task copyDocs(type: Copy) {
    from 'src/main/doc'
    into 'build/target/doc'
}
复制代码

二、依赖关系

Gradle中Task存在着依赖关系,在执行过程当中会先执行所依赖的任务,再执行目标任务。

task hello {
    doLast {
        println 'Hello world!'
    }
}
Task intro(dependsOn: hello) {
    doLast {
        println "I'm Gradle"
    }
}
复制代码

执行结果

> Task :hello
Hello world!

> Task :intro
I'm Gradle 复制代码

三、分组和描述

task能够增长分组和说明

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"
    }
}
复制代码

输入./gradlew tasks

Custom Group 1 tasks
--------------------
hello - This is the Hello Task

Custom Group 2 tasks
--------------------
intro - This is the intro Task
复制代码

4.获取已经定义的任务,并增长执行处理

在上面已有的Task hello中增长执行处理。

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"
    }
}

复制代码

执行./gradlew intro获得以下输出

> Task :hello
prepare to say
Hello world!

> Task :intro
I'm Gradle 复制代码

6、自定义构建功能

一、buildTypes

能够利用这里的属性,针对debug版本和release版本对包名进行修改。具体使用以下:
1)对applicationId进行修改,使其能同时安装debug版本和release版本

android {
    ...
    buildTypes {
        debug {
			// bebug版本包名为xxx.xxx.xxx.debug
			applicationIdSuffix ".debug"
        }
        ...
    }
}
复制代码

2)对release版本进行签名配置

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

        }
    }
复制代码

二、productFlavors

能够根据该属性进行生成不一样的APK版本,版本能够携带不一样的特性,最经常使用的就是多渠道打包、根据不一样环境生成不一样APK。

productFlavors{
		productA{
        	// 定义特定版本号
        	// applicationIdSuffix ".a"
			applicationId "xxx.xxx.xxx.a"
			// 定义特定版本名称
			versionName "version-a-1.0"
			// 定义特定的BuildConfig
			buildConfigField("String","CUSTUMER_CONFIG","xaxaxaxa")
		}
		productB{
			applicationId "xxx.xxx.xxx.b"
			versionName "version-b-1.0"
			buildConfigField("String","CUSTUMER_CONFIG","xbxbxbxb")
		}
	}
	
dependencies{
	...
	// 特定版本依赖
	productACompile 'io.reactivex.rxjava2:rxjava:2.0.1'
	...
}

复制代码

三、增长自定义处理

将生成的apk以自定义命名复制到自定义文件夹

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
                    }
                }
            }
        }
    }
复制代码

7、总结

到这里,其实其实刚刚是使用Gradle的开始,咱们能够操做Gradle完成一些本身的需求,但想对外提供Gradle插件还须要一些功夫,接下来还要继续对android plugin源码进行研读,也找一些有Gradle插件的开源项目进行学习,如Replugin、Tinker,进一步提升本身对Gradle的认识。

关注微信公众号,最新技术干货实时推送

相关文章
相关标签/搜索