前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android编译解析-Kapt

Android编译解析-Kapt

作者头像
烧麦程
发布2022-05-10 20:53:01
2K0
发布2022-05-10 20:53:01
举报
文章被收录于专栏:半行代码半行代码

正题

上篇说到 Android 在编译过程中发起 kotlin 编译的流程。今天分享一下关于 Kotlin 编译里面比较重要的相关步骤:kapt。在 kotlin 里,需要使用 apt 的话,需要使用 kapt 插件来替代 annotationProcessor 的声明:

代码语言:javascript
复制
apply plugin: 'kotlin-kapt'
  
// 依赖
kat 'xx.xx:xxx'  

在编译过程中编译任务会变成下面这样:

kapt 分成两部

generateStubs

生成 stubs 文件, 因为 apt 无法识别 Kotlin 语法,所以 kapt 第一步需要把 Kotlin 文件转换成 apt 可以识别出来的 Java 文件。

kapt 需要把 kotlin 文件生成一个 Java 编译可以认识的产物。但是直接翻译成 Java 文件是没有必要的,这里会生成一个和 kotlin 类类名、字段、方法签名一样但是没有具体实现的 Java 文件。Java文件存在module/build/tmp/kapt3/stubs/这个目录里面。

例如我们写一个 Kotlin 类:

代码语言:javascript
复制
class AKtDog {
    private val TAG = "AKtDog"

    fun playWith(age: Int): Int {
        println(age)
        println("2222")
        println("1111")
        return 11
    }
}

kapt会生成这个 Kotlin 类的 stub 文件:

代码语言:javascript
复制
import java.lang.System;

@kotlin.Metadata
public final class AKtDog {
    private final java.lang.String TAG = "AKtDog";
    
    public AKtDog() {
        super();
    }
    
    public final int playWith(int age) {
        return 0;
    }
}

这里生成的 Java 文件的 playWith 方法里其实是没有逻辑的,所以只是一个壳,把类的 abi 提供给 apt。

这个 task 的源码可以在 kotlin 的源码里面找到: org.jetbrains.kotlin.gradle.internal.KaptGenerateStubsTask

这个 Task 继承自 org.jetbrains.kotlin.gradle.tasks.KotlinCompiler, 说明生成 stubs 的流程其实是遵循了 Kotlin编译的过程的。

这里调用 Kotlin 编译的时候会带入 kapt 相关的命令行参数:-Xplugin=xx/xx/xx/kotlin-annotation-processing-gradle-x.x.x-sources.jar

generateStubs 也支持增量编译,相关的编译缓存内容也和 Koltin编译一样:

kapt build

kotlin apt 编译,执行 apt 流程生成代码。

那么 apt 是咋触发的呢,这个也可以在 Kotlin 的源码里面找到。这部分的 task 源码在 org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask里面

在它的 @TaskAction 里面会调用 kapt 的任务:org.jetbrains.kotlin.gradle.internal.KaptExecution:

代码语言:javascript
复制
// 定义 kapt class
private fun kaptClass(classLoader: ClassLoader) = Class.forName("org.jetbrains.kotlin.kapt3.base.Kapt", true, classLoader)
val kaptMethod = kaptClass(kaptClassLoader).declaredMethods.single { it.name == "kapt" }
kaptMethod.invoke(null, createKaptOptions(kaptClassLoader))

这里执行的是 org.jetbrains.kotlin.kapt3.base.Kapt对象的 kapt 方法。这个里面就会和 Java 编译一样去调用我们的 apt 了:

代码语言:javascript
复制
val annotationProcessingTime = measureTimeMillis {
  kaptContext.doAnnotationProcessing(
    javaSourceFiles,
    processors.processors,
    binaryTypesToReprocess = collectAggregatedTypes(kaptContext.sourcesToReprocess)
  )
}

禁用kapt

通过上面的分析,我们可以发现,其实 apt 本身支持了增量编译,编译速度还是非常快的。

但是kapt生成stubs文件的流程在增量编译的时候就不是那么可控了,和 kotlin 编译一样,会有各种case让增量失效。那么全量重新生成一遍 stubs 文件那编译速度就有点慢了。

不过转念一想,我们是不是可以增量编译的时候在没有 apt 相关注解变动的时候直接禁用 generateStubs 这个 task呢?理论上这么做是可以的,所以我进行了一下尝试,在 gradle plugin 里面去寻找 generateStubs 的任务,然后禁用这个任务。最后我这里试验是成功的。具体做法如下:

代码语言:javascript
复制
project.tasks.whenTaskAdded {
  if (it.name.contains("kaptGenerateStubs")) {
    it.enabled = false
  }
}

这里我们把 KaptGenerateStubs 相关的 task 的 enabled设置为 false,最后编译的时候,这个task则会跳过,控制台的输出如下:

代码语言:javascript
复制
> Task :amodule:kaptGenerateStubsDebugKotlin SKIPPED

总结

今天分享了一下 kapt 相关的内容,我们可以从中相关的内容了解 kapt 的原理。在未来我们可以及时拥抱 ksp 等新技术,直接在 kotlin 的 AST 中处理,来提升我们的编译效率。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-05-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 半行代码 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 正题
    • generateStubs
      • kapt build
      • 禁用kapt
      • 总结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档