之前Android Gradle的神奇之处 ---- Gradle构建和Task引入对Task任务做了简单的介绍,本节着重介绍自定义Task任务之间的联动
1 Task任务的输入输出
通常,Task任务的输入就是某个Task任务的输出,例如apk打包,就经过dex打包,apkbuilder压缩,apk签名等,每个流程的输出都是下一个流程的输出,在自定义Task任务中,@Input和@OutputFile代表当前的输入和输出
1.1 @Input @OutputFile
class MyTask extends DefaultTask{
@Input
def inputPath
@OutputFile
def outFile
MyTask(){
group 'custome'
println 'MyTask 构造方法 配置阶段执行'
}
@TaskAction
def Action(){
println 'MyTask 执行了'
}
}
如果在没有指定输入和输出的情况下,执行task任务会报错
Type 'MyTask' property 'inputPath' doesn't have a configured value.
使用@Optional注解,代表当前属性可选,可以选择不传数据,就不会报错
1.2 inputs.file outputs.files
如果不想使用1.1中的注解,还可以通过其他的方式,给当前的task设置输入和输出
inputs.files file('build.gradle')
outputs.files file('text.txt')
使用inputs.files和outputs.files可以给当前任务输入或者输出一个文件
如果想要获取输入的文件,因为当前只输入一个文件,可以使用singleFile获取文件内容
inputs.files.singleFile
这里进行了一个简单的读写操作,将输入文件的内容复制到了输出文件中
@TaskAction
def Action(){
println 'MyTask 执行了'
println inputs.files.singleFile
def inFile = inputs.files.singleFile
def outFile = outputs.files.singleFile
outFile.createNewFile()
outFile.text = inFile.text
}
2 系统内置任务
2.1 Zip
Zip是系统自带的一个zip任务,能够把目标文件夹的内容打成zip包发布
task zipTask(type: Zip){
archiveName 'build.zip'
destinationDir project.buildDir
from project.buildDir
}
这里有几个参数需要明确一下 from:待压缩的文件所在的文件夹,project.buildDir为build文件夹,每个模块下都有自己的build文件夹 archiveName:压缩后的文件名 destinationDir:压缩后的文件存放位置
2.2 packageDebug
在app工程下,build/outputs下回生成对应的 debug apk 或者 release apk,如果想要获取build文件夹下的outputs文件夹,需要使用packageDebug任务,那么首先就要获取packageDebug任务;
tasks.getByName('packageDebug')
A problem occurred evaluating project ':app'.
> Task with name 'packageDebug' not found in project ':app'.
任务是在配置阶段生成的,如果直接拿是拿不到的,之前讲到过的钩子函数就可以帮助在任务创建之后,拿任务,这个时候肯定拿的到
project.afterEvaluate{
println tasks.getByName('packageDebug')
task zipTask(type: Zip){
archiveName 'build.zip'
destinationDir project.buildDir
from tasks.getByName('packageDebug').outputs.files
}
}
3 Task的增量构建
当多次执行gradle脚本构建的时候,第一次所有的task任务都会执行,但是如果同样的task再执行一次,就不会再次执行action中的操作,而是默认使用上次构建的,像Task B UP-TO-DATE,就是没有执行
> Task :B UP-TO-DATE
> Task :A
A do First
> Task :finalizeTask UP-TO-DATE
> Task :C
C do First
C do doLast
这就是增量构建,增量构建的原理就是监控input的变化,只有input发送变化了,才重新执行task任务,否则gradle认为可以重用之前的执行结果。
4 Project
每个moudle都对应了一个project对象,因通过project对象就可以对每个项目做配置
project(':app'){
apply plugin:'com.android.application'
apply plugin:'kotlin-android'
apply plugin:'kotlin-kapt'
println("组件化")
def config = rootProject.ext.androidId
def dependency = rootProject.ext.dependency
android {
compileSdk config.compileSdk
defaultConfig {
applicationId "com.study.modulelization"
minSdk config.minSdk
targetSdk config.targetSdk
versionCode config.versionCode
versionName config.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("Boolean", "isRelease", "${rootProject.isRelease}")
kapt {
arguments{
arg("moduleName","app")
}
}
}
buildTypes {
debug {
buildConfigField("String", "debugUrl", "\"${rootProject.url.DEBUG} \"")
}
release {
buildConfigField("String", "releaseUrl", "\"${rootProject.url.RELEASE}\"")
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation project(path: ':complier_api2')
dependency.each { k, v -> implementation v }
if (rootProject.isRelease) {
implementation project(path: ':register')
implementation project(path: ':lay_router')
kapt project(path: ':lay_compiler')
implementation project(':complier_api2')
implementation project(':commonlib')
}
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1'
implementation 'androidx.lifecycle:lifecycle-livedata-core-ktx:2.4.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
}
}
这里通过project(‘模块名称’),在闭包中就可以对当前模块做配置,其实这样就代替了app模块中的build.gradle
通过subprojects则是可以获取到全部的子工程的project对象
4.1 ext属性扩展
ext属性扩展,其实就是对project对象做属性扩展
# ext.gradle
ext {
prop1 = 'prop1'
}
在根 build.grdle中做了属性扩展,实际上就是对rootProject做了扩展,那么每个子模块都能拿到这个rootProject,相当于在build.grdle中声明了一个变量
# 根 build.gradle
project.ext.prop2 = 'prop2'
在子模块中,去获取这个变量,注意是使用rootProject
# register模块 --- build.gradle
println rootProject.prop1
println rootProject.prop2
输出
prop1
prop2
1~ 对于所有对象都可以属性扩展,通过ext{ }闭包或者直接赋值 ext.prop = … 2~ 由谁调用,就是给谁做扩展,例如project中调用,就是给project做扩展,一般情况下都是 3~ 在build.gradle中,默认就是给当前工程的project做扩展,在根build.gradle中就是给rootProject做扩展,所有的moudle都可以使用这个扩展属性
5 Gradle插件
gradle插件就是提供给gradle构建工具,在编译时的依赖项;主要的目的就是抽取公共的构建业务,达到复用的效果
像系统提供的一些任务,都是剥离出来可复用的,之前常见的Zip、Clean,都是插件中的任务;Gradle插件共分为2种,接下来讲解第一种
5.1 脚本插件
脚本插件是比较常见的一种插件,在之前就已经写过一些,比如抽离一个压缩zip的任务,可以提供给某个模块使用
# zip.gradle
project.afterEvaluate{
task zipTask(type:Zip){
archiveName 'zip01.zip'
destinationDir "${project.buildDir}/custome"
from tasks.getByName('packageDebug').outputs.files
}
}
# app/build.gradle
apply from:'/****/zip.gradle'
通过apply的方式就完成了脚本的注入,可以执行task任务
5.2 二进制插件
二进制插件,就是将脚本打成jar包,实现jar包依赖
# 根 build.gradle
class MyPluginTask implements Plugin<Project>{
@Override
void apply(Project target) {
println 'MyPluginTask apply'
}
}
二进制插件实现了Plugin接口,重写apply方法,在apply方法中做业务打包,使用方式还是apply,只不过是使用plugin作为key
# 根 build.gradle
apply plugin: MyPluginTask
像这种方式是只能在一个模块中使用这个二进制插件,如果想要所有的项目共享,那么就需要将这个插件上传maven私服,那么在使用的时候,将这个插件拉取下来使用
5.2.1 buidSrc
创建一个buildSrc文件夹,注意!不能写错,然后重新rebuild一下工程 然后,需要创建一个src/main/java文件夹,如果使用Java编写就创建java文件夹,使用groovy编写就创建groovy文件夹
public class MyTargetPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
System.out.println("这是自定义的二进制插件"+project.getClass());
}
}
当创建这个二进制插件之后,就可以在任意一个moudle中使用
# register/build.gradle
apply plugin: MyTargetPlugin
我们可以看到,在buildSrc的libs文件夹下有一个jar包,这个jar包就可以上传到maven仓库
5.2.2 buildSrc简介
buildSrc是Android工程默认的一个插件工程,有且仅有一个,能够直接实现插件的封装
settingsEvaluated
projectsLoaded
> Task :buildSrc:compileJava UP-TO-DATE
> Task :buildSrc:compileGroovy NO-SOURCE
> Task :buildSrc:processResources
> Task :buildSrc:classes
> Task :buildSrc:jar
> Task :buildSrc:generateSourceRoots UP-TO-DATE
> Task :buildSrc:assemble
> Task :buildSrc:compileTestJava NO-SOURCE
> Task :buildSrc:compileTestGroovy NO-SOURCE
> Task :buildSrc:processTestResources NO-SOURCE
> Task :buildSrc:testClasses UP-TO-DATE
> Task :buildSrc:test SKIPPED
> Task :buildSrc:check SKIPPED
> Task :buildSrc:build
> Configure project :
资源回收
MyTask 构造方法 配置阶段执行
我们可以看到,在Gradle配置之前,就已经在编译阶段去编译这个buildSrc项目,然后配置到classpath下面,那么在gradle的配置阶段,就可以直接使用buildSrc中生成好的插件去做操作处理
使用buildSrc的好处在于,如果要修改插件内的内容,可以不用等发版,就直接在本地实现配置依赖,不用上传maven仓库,例如在插件中创建一个zip任务,不需要发版,在app模块中直接依赖即可
public class MyTargetPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
System.out.println("这是自定义的二进制插件"+project.getClass());
project.afterEvaluate(new Action<Project>() {
@Override
public void execute(Project project) {
File buildDir = project.getBuildDir();
Task packageDebug = project.getTasks().getByName("packageDebug");
Map<String,Class<?>> map = new HashMap<>();
map.put("type", Zip.class);
Zip zipTask = (Zip) project.task(map,"pluginTask");
zipTask.setArchiveName("zip02.zip");
zipTask.setDestinationDir(new File(buildDir.getAbsolutePath()));
zipTask.from(packageDebug.getOutputs().getFiles());
}
});
}
}
通过Java也可以实现,在gradle中创建zip任务的流程,就是写起来比较繁琐
# app/build.gradle
apply plugin: MyTargetPlugin
|