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

栏目: Android · 发布时间: 6年前

内容简介:通过上一节Groovy其实和js、python、kotlin类似,弱类型、闭包等特性,又完全兼容java,学习起来较为简单。具体可以专题进行学习Groovy语法。可以说每一个build.gradle都是一个Project对应项目中就是某一个module,直接在其中定义的属性或者方法都可以使用project来调用。如

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

二、Groovy语法简介

Groovy其实和js、 python 、kotlin类似,弱类型、闭包等特性,又完全兼容java,学习起来较为简单。具体可以专题进行学习Groovy语法。

1、与 java 比较

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

2、Groovy高效特性

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

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



复制代码

三、Gradle中的基础概念

1、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是一个全局变量的意思。

2、Task

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

3、Gradle构建声明周期

Gradle的声明周期分为三段:

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

四、Gradle脚本的引入

引入gradle脚本,可以实现对gradle代码的复用,减少维护成本。可以通过 apply from: 'other.gradle' 来对other.gradle进行引入。日常开发中,我们可以通过如下几点优化我们的项目:

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

在gradle中有很多配置项,如版本号,版本名称,最小支持SDK版本,是否使用混淆,依赖的第三方库等。这些配置很有可能在项目中复用,如需修改,可能会导致遗漏,将这些配置提取出来,供各个build.gradle使用。具体如下:

1、在根目录新建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"]}"
    ]
    
}
复制代码

2、在根目录build.gradle中增加 apply from:"config.gradle"
3、修改module的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"]
}
复制代码

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

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

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

复制代码

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

apply from:"../default.gradle"

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

}

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

}

复制代码

3、对build.gradle文件前后做一个对比

修改后的好处在于,比如在工程中多处使用support包中的依赖,一次修改版本号即可对全部工程生效,降低了维护成本。

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

五、自定义Task任务

1、定义任务

最常用的写法,执行 ./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'
}
复制代码

2、依赖关系

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

复制代码

3、分组和描述

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

复制代码

六、自定义构建功能

1、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

        }
    }
复制代码

2、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'
	...
}

复制代码

3、增加自定义处理

将生成的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
                    }
                }
            }
        }
    }
复制代码

七、总结

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


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Head First Design Patterns

Head First Design Patterns

Elisabeth Freeman、Eric Freeman、Bert Bates、Kathy Sierra、Elisabeth Robson / O'Reilly Media / 2004-11-1 / USD 49.99

You're not alone. At any given moment, somewhere in the world someone struggles with the same software design problems you have. You know you don't want to reinvent the wheel (or worse, a flat tire),......一起来看看 《Head First Design Patterns》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

html转js在线工具
html转js在线工具

html转js在线工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试