前言
前一阵子帮事务同学处理了个代码问题,其实挺有意思的,就计划和咱们分享下这个内容。
先简略的介绍下背景,事务同学写了个apt
的框架,然后里边包括一个注解的库,而注解库中需求使用到Android
源码中的View
。可是由于这是一个Java Library
,无法直接将安卓的源码添加到依靠中,就无法引用到View
。然后他们为了处理这个问题,又创建了一个库,然后生成了一个同包名的Android View
,相似下图这总,然后compileOnly
这个库。
由于这个模块内有了这个View
,事务同学在后续调试体系源码的时分都会进到这个造假出来的View上去了,就产生了很大的搅扰效果。原因呢其实便是由于这个类呗添加到sourceSet
中了,同名类的情况下会优先使用上层加载的。
这种在java库内需求造假出一些Android View
,就变成了一个很好玩的东西了。接下来咱们就经过gradle的一些简略的操作,来把这个坑填上。
具体代码能够看下这个 Router-Android
Gradle Java Compiler Task
在build.gradle
中,咱们能够声明一个task
使命,然后声明这个使命承继的类型,让它变成一个能够java编译代码的使命。
task("stubLib", JavaCompile::class) {
source(file("src/stub/java"))
classpath = project.files(getAndroidJar("32"))
// libraries
destinationDirectory.set(File(project.buildDir, "/tmp/stubLibs"))
}
fun getAndroidJar(compileSdkVersion: String): String {
var androidSdkDir =
System.getenv(com.android.tools.analytics.Environment.EnvironmentVariable.ANDROID_SDK_HOME.key)
if (androidSdkDir.isNullOrEmpty()) {
val propertiesFile = rootProject.file("local.properties")
if (propertiesFile.exists()) {
val properties = Properties()
properties.load(propertiesFile.inputStream())
androidSdkDir = properties.getProperty("sdk.dir")
}
}
if (androidSdkDir.isNullOrEmpty()) {
throw StopExecutionException("please declares your 'sdk.dir' to file 'local.properties'")
}
val path = "platforms${File.separator}android-${compileSdkVersion}${File.separator}android.jar"
return File(androidSdkDir.toString(), path).absolutePath
}
看起来这段代码就比较简略。首先咱们声明晰一个gradle task(gradle根底概念 有爱好的能够自己去了解下),这个Task承继自JavaCompile
,然后输入的是src/stub/java
这个文件夹下的内容,classpath是android
源代码,输出是工程的build//tmp/stubLibs
文件夹。
介绍完了Task的声明之后,它会做些什么。这个声明的使命会基于他的输入内容,然后履行java编译使命,最后把.class输出到输出的文件夹下。
获取Android.jar
这个比较简略,其实Android.jar是要区别compile版本的,这些都放在android sdk下。相似这种/Android/sdk/platforms/android-32/android.jar
。代码便是上面的getAndroidJar
。
class -> jar
上面这个JavaCompile
使命负责的便是将java
转变成class
文件,可是并没有方法直接被工程使用。由于工程内咱们只能依靠于jar或者aar的依靠方式,而没有方法使用class文件。所以咱们要做的便是把这些class经过别的一个使命压缩成一个jar包。
task("stubLibsJar", Jar::class) {
archiveBaseName.set("stub")
archiveVersion.set("1.0")
from(tasks.getByName("stubLib"))
include("**/*.class")
}
这个也是Gradle
内供给的一个使命,能够从类型中看出来便是一个转化Jar::class
的使命。其中jar的名字叫stub,版本号1.0。内容则来自前置的使命stubLib
(咱们上面声明的那个使命)。然后包括里边一切的.class文件。之后把这些内容都转化成一个jar包输出。
dependencies中履行使命
上面的这个方法已经让咱们能够在一个"java-library"
中使用安卓编译出来的jar包了。可是咱们的代码内还没有方法建立索引,由于configuration内并不存在这个jar包,咱们需求把这个编译产物添加到dependencies中去才行。
dependencies {
// implementation fileTree(dir: 'libs', include: ['*.jar'])
val stub = tasks.getByName("stubLibsJar").outputs.files
compileOnly(stub)
}
先从taskManager中获取到这个使命,然后取出这个使命的output的文件,然后compileOnly
这个jar。
经过这种方式咱们就能够活学活用gradle的特性,先造假出一些咱们想要的假的体系类,然后编译成jar包,之后仅在编译时使用这些,这样这些类在实际运行时就会被替换成android.jar中的类了。
这样一开始咱们说的工程内的问题就被咱们完美的规避和处理了。
结束
本文能够当做一个gradle task
的入门文章,经过几个简略的比如给咱们介绍下。我之前也关注了些Gradle相关的文章,一般介绍的gradle task的文章就有点太无聊了,很难有用一个生动的比如和各位说明为什么需求task,输入输出的含义是什么,希望本文对咱们有所帮助。
别的作为一个老卷逼了,最近在做整个工程的kotlin+compose+androidx的升级作业,进度仍是挺顺利的,能不能顺利提桶就看这一出了啊。