12.4.8 多轮处理
3 分钟阅读
12.4.8 多轮处理
KSP 支持多轮处理,也就是在多轮中处理文件。每轮处理的输出会作为后续各轮的额外输入。
要使用多轮处理,请让 SymbolProcessor.process() 以 List<KSAnnotated> 的形式返回被推迟的符号。KSP 会在下一轮处理这些符号。
要推迟无效符号,请用 KSAnnotated.validate() 过滤它们,例如:
| |
当某一轮完全没有生成新文件时,多轮处理就会结束。如果有被推迟的符号仍未处理,KSP 会为每个仍有未处理推迟符号的处理器记录一条错误。
把符号推迟到下一轮
当需要其他处理器提供更多信息时,处理器可以把符号推迟到后面的轮次。处理器可以跨多轮持续推迟某个符号,直到所需信息可用。一旦信息可用,处理器就可以处理该符号。
只在以下情况下才推迟符号:
在处理该符号之前还需要额外信息。
该符号来自源代码。
注意: 绝不要推迟来自 classpath 的符号。KSP 会自动过滤掉 classpath 符号。
例如,一个为带注解的类生成 builder 的处理器,可能要求所有构造函数参数类型都能解析为具体类型。在第一轮中,某个参数类型可能无法解析。而在后面的某一轮中,由于期间生成的文件,它可能变得可以解析。处理器那时就可以处理该类的。
校验符号
校验是一种判断是否应把符号推迟到后续轮次的便捷方式。处理器应当定义正确处理某个符号所需的信息。
提示: 校验通常需要类型解析,而这是有开销的。只检查处理该符号所必需的信息。
默认的校验行为可能并不适合所有用例。要自定义校验,请使用 KSValidateVisitor 并提供一个用于选择待校验符号的 predicate lambda。
在实现自定义校验时,请使用 KSType.isError 判断类型是否有效。如果 isError 为 true,说明 KSP 无法解析该类型。利用这一信息来决定是否把处理推迟到后续轮次。
访问文件与符号
新生成的文件和已有文件都可以通过 Resolver 访问。
KSP 提供两个用于访问文件的 API:
Resolver.getAllFiles()返回先前已有文件和新生成文件的列表。Resolver.getNewFiles()只返回上一轮生成的文件。
请把 Resolver.getSymbolsWithAnnotation() 作为获取相关符号的主要入口。
在每一轮中,Resolver.getSymbolsWithAnnotation() 只返回新生成文件中的符号,以及上一轮推迟的符号。这有助于避免不必要的重复处理。
处理器的实例化
KSP 只会创建一次处理器实例。你可以在处理器实例中存储信息,并在多轮之间复用它。
不过,并非所有 KSP 符号都能跨轮复用。当处理器生成新文件时,符号解析结果可能发生变化,从而影响此前已解析符号的有效性。
注意: 只使用当前轮次传给处理器的那个
Resolver实例。不要保存Resolver并跨轮复用。
错误与异常处理
错误
处理器通过调用 KSPLogger.error() 报告错误。
当处理器报告错误时,KSP 会调用 SymbolProcessor.onError() 而不是 SymbolProcessor.finish()。当前轮次结束后处理就会停止。
在这一轮中,其他处理器会正常继续处理。KSP 会在所有处理器完成当前轮次之后才处理错误。
异常
KSP 会区分由 KSP 抛出的异常和由处理器抛出的异常。两类异常都会立即终止处理,并通过 KSPLogger 记录为错误。
注意: 请把 KSP 抛出的异常报告给 KSP 开发者以便调查。请在 KSP 问题跟踪器中创建 issue。
在发生错误或异常的那一轮结束时,KSP 会对所有处理器调用 SymbolProcessor.onError()。SymbolProcessor 为 onError() 提供了默认的空实现。重写该方法即可实现自定义的错误处理逻辑。