各种 Code Generator 的功能

上一篇我们有提到用 KAPT 参数去呼叫 纯 Kotlin 和 Android 的 code generator ,这篇要延续这个主题,继续来讲一下会怎麽样去呼叫它们来产生程序码。

我们在 annotation processor 里面有定义了 process 的方法:

@SupportedOptions("debug")
@AutoService(Processor::class)
class RssAnnotationProcessor : AbstractProcessor() {
	// 略
	override fun process(typeElementSet: MutableSet<out TypeElement>?, roundEnvironment: RoundEnvironment?): Boolean {
      val logger = Logger(processingEnv.messager)
      val isPureKotlinParser = processingEnv.options[KAPT_OPTION_KEY]?.toBoolean() ?: false
      if (isPureKotlinParser) {
          generateClassesForKotlin(logger, roundEnvironment)
      } else {
          generateClassesForAndroid(logger, roundEnvironment)
      }
      return true
  }
	// 略
}

其中的 generateClassesForKotlin 是产生 Kotlin 的 extension 和 parser 的地方。

private fun generateClassesForKotlin(logger: Logger, roundEnvironment: RoundEnvironment?) {
    if (!isExtensionGenerated) {
        KotlinExtensionGenerator(logger).generate().writeTo(processingEnv.filer)
        isExtensionGenerated = true
    }

    generateParsers(roundEnvironment) { isRoot, element ->
        logger.log("[RssAnnotationProcessor][generateClassesForKotlin] isRoot = $isRoot, element = $element")
        KotlinParserGenerator(
            element = element,
            isRoot = isRoot,
            logger = logger
        ).generate().writeTo(processingEnv.filer)
    }
}

generateClassesForAndroid 则是负责产生 Android 的 extension 和 parser 。

private fun generateClassesForAndroid(logger: Logger, roundEnvironment: RoundEnvironment?) {
    // This 'process' method could be called multiple times, so we use a flag to prevent it generate multiple times.
    if (!isExtensionGenerated) {
        AndroidExtensionGenerator(logger).generate().writeTo(processingEnv.filer)
        isExtensionGenerated = true
    }
    generateParsers(roundEnvironment) { isRoot, element ->
        AndroidParserGenerator(
            element = element,
            isRoot = isRoot,
            logger = logger
        ).generate().writeTo(processingEnv.filer)

        if (isRoot) {
            AndroidReaderParserGenerator(
                rootClassName = element.simpleName.toString(),
                rootClassPackage = element.getPackage(),
                logger
            ).generate().writeTo(processingEnv.filer)
        }
    }
}

因为 process 方法在 annotation processor 里面会被呼叫多次,每当有类别的 annotation 被扫到,就会被呼叫一次,每次都是不同的 generation cycle ,被产生的 class 也会被放在不同的档案里面。

Android 的部分比较特殊的地方是它有产生自己的 reader ,它跟 parser 的不同在於它是使用产生後的 parser 去做其他的处理,像是资料快取和取得网路的 RSS 档案来源,等於是把 parser 再包一层好用的 API ,这样在 Android 上面可以更简单地被使用。资料快取的部分,android 会用 database 以 url 当成是 key 去把资料存起来,也有设置过期时间,这样可以确保不会抓到过期的资讯,也让快取可以发挥作用。当然,外部 API 还是要按照自己的需求设计,也可以设计成让使用者可以决定要不要使用快取。在取得网路 RSS 来源的部分,是使用了 OkHttp 直接去取得档案内容。这些 Kotlin 和 Android 的 generator 会在後面的文章一一为各位讲解。


<<:  D12 - 用 Swift 和公开资讯,打造投资理财的 Apps { 加权指数K线图分析 }

>>:  Rust-Match控制流运算子

那些被忽略但很好用的 Web API / RequestIdleCallback

时间管力大师就是要忙里偷闲 各位应该知道 JavaScript 是单执行绪(单线程)的程序语言,也...

[FGL] Error: Invalid hello message

出现频率:极少数客户 (但是若有,该主机就会常常出现此讯息) 成因:目前未能完全确认原始成因,但是...

Day 05 - TypeScript 语法

字串 string / 数字 number let userName: string; // 将变数...

Android Studio初学笔记-Day4-ConstaintLayout

今天要再来介绍一个我个人也蛮常使用的一布局 ConstaintLayout (约束布局) 承前两篇这...

[Day 12] Forensics 小挑战

今天心情蛮好的,期待叻2周终於等到这天了,生平第一次染发:) 这篇文有一半是我边染发变打出来的~ 染...