首先,简述一下让Java的自定义注解支持增量编译的步骤:

在注解模块的

模块\src\main\resources\META-INF\gradle\incremental.annotation.processors

文件中填写:

包名.注解处理器1名称,isolating 包名.注解处理器2名称,isolating

是不是与在 META-INF\serveices\javax.annotation.processing.Processor文件中声明注解处理器很像呢?只不过后面多了,isolating,或者,dynamic,或者,aggregating这三个变量之一。

孤立?动态?聚合?这什么鬼命名法!

就用孤立好了。如果需要理解“内涵”,文档在这里:https://docs.gradle.org/current/userguide/java_plugin.html#sec:incremental_annotation_processing

摘抄并翻译部分如下:

"Isolating" annotation processors

The fastest category(速度最快的类别), these look at each annotated element in isolation(单独处理每个被标记的元素), creating generated files(生成文件) or validation messages(或生成校验信息) for it. For instance an EntityProcessor could create a Repository for each type(每个类) annotated with @Entity

"dynamic" annotation processors

If your processor can only decide at runtime whether it is incremental or not(运行时才能判断是否支持增量编译), you can declare it as “dynamic” in the META-INF descriptor and return its true type at runtime using the Processor#getSupportedOptions() method

"Aggregating" annotation processors

These can aggregate several source files (几个源文件一起合成一个或多个)into one or more output files or validation messages(输出文件或校验信息). For instance, a ServiceRegistryProcessor could create a single ServiceRegistry with one method for each type annotated with @Service…(这句子……看不大懂……即使有多个类被@Service标注,ServiceRegistryProcessor 也只创建一个含有一个方法的ServiceRegistry,大概这意思吧,关键后面“with one method”好XBXBXB啊…… )

with one method

有一个方法?用一个方法?谁有?谁用?谁用谁?谁有谁?多种理解方式,英语是劣等语言。

三个参数都有限制,比如我遇到的,文档说dynamic、isoltaiting都不支持使用Trees API:

Both categories have the following limitations:

  • They must generate their files using the Filer API. Writing files any other way will result in silent failures later on, as these files won’t be cleaned up correctly. If your processor does this, it cannot be incremental.
  • They must not depend on compiler-specific APIs like com.sun.source.util.Trees. Gradle wraps the processing APIs, so attempts to cast to compiler-specific types will fail. If your processor does this, it cannot be incremental, unless you have some fallback mechanism.(Gradle 包裹了编译器相关的API,暴露出相同的接口,所以不可将之转化为这些的编译器特定的类。如果你的注解处理器不管不顾仍然我行我素,它将不会支持增量编译,除非你有回退机制!)
  • If they use Filer#createResource, the location argument must be one of these values from StandardLocation: CLASS_OUTPUT, SOURCE_OUTPUT, or NATIVE_HEADER_OUTPUT. Any other argument will disable incremental processing.

所以,启用增量编译后,下面原本没有报错的代码就会出现问题:

@Override
	public void init(final ProcessingEnvironment procEnv) {
		super.init(procEnv);
		JavacProcessingEnvironment javacProcessingEnv = (JavacProcessingEnvironment) procEnv;
		this.elementUtils = javacProcessingEnv.getElementUtils();
		this.maker = TreeMaker.instance(context=javacProcessingEnv.getContext());
		this._trees = Trees.instance(procEnv);
	}

开启增量编译后,参数procEnv不再直接就是JavacProcessingEnvironment,而是Gradle的包裹类IncrementalProcessingEnvironment,所以上述代码会出现转换错误,无法通过编译。

不过,可以用反射得到到原本的 JavacProcessingEnvironment,这也是我通过打印的方式得知的。

CMN.Log(procEnv.getClass().getDeclaredFields());

打印出来,第一个成员变量就是

JavacProcessingEnvironment delegate。

所以,出错的代码需修正为:

@Override
	public void init(final ProcessingEnvironment procEnv) {
		super.init(procEnv);
		this.elementUtils = (JavacElements) procEnv.getElementUtils();
		JavacProcessingEnvironment jcEnv = null;
		if (procEnv instanceof JavacPro cessingEnvironment) {
			jcEnv = (JavacProcessingEnvironment) procEnv;
		} else {
			try {
				Field f = procEnv.getClass().getDeclaredField("delegate");
				f.setAccessible(true);
				jcEnv = (JavacProcessingEnvironment) f.get(procEnv);
				// CMN.Log(jcEnv);
			} catch (Exception e) {
				CMN.Log(e);
			}
		}
		if (jcEnv!=null) {
			this.maker = TreeMaker.instance(context=jcEnv.getContext());
			this._trees = Trees.instance(jcEnv);
		}
	}

其中,·maker· 用于构造语句、方法体等等,·_trees· 可用于获取、修改imports。

_trees 获取 imports 的方法参考。

( 测试环境Adnroid Studio 4.0 )


最后说一下这个用来作什么,我是用来自动删除一些不需要的代码,比如androidx支持库中的NightMode,实在没什么用,切换夜间模式是需要重建Activity的,不需要,干脆直接删除代码。手动删除不好更新,用注解自动处理的话,理想情况只需在类上加一个注解就可以,不过往往不完美需要手动fix一二。

删除方法之余,还需要删除import,不然有的地方无法编译。所以找到了上面那篇用Trees修改import的文章。

完整代码:

Metaline | 元线模块

直接使用:

dependencies {
  	compileOnly "com.gitee.knziha:Metaline:1.9"
  	annotationProcessor "com.gitee.knziha:Metaline:1.9"
  	testCompileOnly "com.gitee.knziha:Metaline:1.9"
  	testAnnota
  	tionProcessor "com.gitee.knziha:Metaline:1.9"
  }