前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >伪造出一个假的系统View | Gradle Task

伪造出一个假的系统View | Gradle Task

作者头像
逮虾户
发布2022-09-19 17:00:04
3210
发布2022-09-19 17:00:04
举报
文章被收录于专栏:逮虾户

前言

前一阵子帮业务同学解决了个代码问题,其实挺有意思的,就打算和大家分享下这个内容。

先简单的介绍下背景,业务同学写了个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编译代码的任务。

代码语言:javascript
复制
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包。

代码语言:javascript
复制
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中去才行。

代码语言:javascript
复制
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的升级工作,进度还是挺顺利的,能不能顺利提桶就看这一出了啊。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-07-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • Gradle Java Compiler Task
    • 获取Android.jar
    • class -> jar
    • dependencies中执行任务
    • 结尾
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档