【错误记录】Android 编译时技术报错 ( 注解处理器 process 方法多次调用问题 )

简介: 【错误记录】Android 编译时技术报错 ( 注解处理器 process 方法多次调用问题 )

文章目录

一、报错信息

二、问题分析

三、解决方案


注解处理器 AbstractProcessor 中的 process 方法可能会调用多次 , 在生成代码时 , 一定要注意 , 检测到 注解节点 后再生成代码 ;






一、报错信息


Android 编译时技术 , 使用注解处理器生成代码 , 编译时报如下错误 :


( 该错误不会中断编译 )


javax.annotation.processing.FilerException: Attempt to recreate a file for type com.example.helloworld.HelloWorld
  at com.sun.tools.javac.processing.JavacFiler.checkNameAndExistence(JavacFiler.java:522)
  at com.sun.tools.javac.processing.JavacFiler.createSourceOrClassFile(JavacFiler.java:396)
  at com.sun.tools.javac.processing.JavacFiler.createSourceFile(JavacFiler.java:378)
  at com.squareup.javapoet.JavaFile.writeTo(JavaFile.java:169)
  at kim.hsl.router_compiler.RouterProcessor.process(RouterProcessor.java:91)
  at org.gradle.api.internal.tasks.compile.processing.DelegatingProcessor.process(DelegatingProcessor.java:62)
  at org.gradle.api.internal.tasks.compile.processing.NonIncrementalProcessor.process(NonIncrementalProcessor.java:45)
  at org.gradle.api.internal.tasks.compile.processing.DelegatingProcessor.process(DelegatingProcessor.java:62)
  at org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor.access$401(TimeTrackingProcessor.java:37)
  at org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor$5.create(TimeTrackingProcessor.java:99)
  at org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor$5.create(TimeTrackingProcessor.java:96)
  at org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor.track(TimeTrackingProcessor.java:117)
  at org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor.process(TimeTrackingProcessor.java:96)
  at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:794)
  at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:705)
  at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1800(JavacProcessingEnvironment.java:91)
  at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1035)
  at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1176)
  at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1170)
  at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:856)
  at com.sun.tools.javac.main.Main.compile(Main.java:523)
  at com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:129)
  at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:138)
  at org.gradle.internal.compiler.java.IncrementalCompileTask.call(IncrementalCompileTask.java:74)

image.png





二、问题分析


根据上述报错信息提示 " Attempt to recreate a file " , 尝试重新创建一个文件 , 也就是说之前已经创建了一次文件 ;


注解处理器代码如下 :


package kim.hsl.router_compiler;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import java.io.IOException;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
// 自动注册注解处理器
@AutoService(Processor.class)
// 支持的注解类型
@SupportedAnnotationTypes({"kim.hsl.router_annotation.Route"})
// 支持的 Java 版本
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class RouterProcessor extends AbstractProcessor {
    /**
     * 注解处理器中使用 Messager 对象打印日志
     */
    private Messager mMessager;
    /**
     * 用于写出生成的 Java 代码
     */
    private Filer mFiler;
    /**
     * 该函数在初始化时调用 , 相当于构造函数
     * @param processingEnvironment
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        // 获取打印日志接口
        this.mMessager = processingEnvironment.getMessager();
        mMessager.printMessage(Diagnostic.Kind.NOTE, "Messager Print Log");
        this.mFiler = processingEnvironment.getFiler();
    }
    /**
     * 该函数在注解处理器注册时自动执行, 是处理注解的核心函数
     *
     * Set<? extends TypeElement> set 参数 : 该集合表示使用了相关注解的节点的集合
     *
     * @param set
     * @param roundEnvironment
     * @return
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        for (TypeElement typeElement: set){
            mMessager.printMessage(Diagnostic.Kind.NOTE, "SupportedAnnotationTypes : " + typeElement.getQualifiedName());
        }
        // 生成 public static void main(String[] args) 函数
        MethodSpec main = MethodSpec.methodBuilder("main")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(void.class)
                .addParameter(String[].class, "args")
                .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
                .build();
        // 指定 public final class HelloWorld 类
        TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addMethod(main)
                .build();
        // 正式在 "com.example.helloworld" 包名下创建 HelloWorld 类
        JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
                .build();
        try {
            javaFile.writeTo(mFiler);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }
}




在 process 方法中 , 使用 JavaPoet 生成 Java 代码 ;


上述 process 方法应该是调用 3 33 次 , 调用第一次时生成了 com.example.helloworld.HelloWorld 源码 , 但是后面又调用了 2 22 次 , 后面调用的 2 22 次直接报上述 " javax.annotation.processing.FilerException: Attempt to recreate a file for type com.example.helloworld.HelloWorld " 错误 ;






三、解决方案


AbstractProcessor 中的 process 方法调用了 3 33 次 , 但是只有 1 11 次 Set<? extends TypeElement> set 注解参数不为空 , 这里检测到注解后 , 再生成 Java 代码即可 ;



修改后的源代码如下 :


package kim.hsl.router_compiler;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import java.io.IOException;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
// 自动注册注解处理器
@AutoService(Processor.class)
// 支持的注解类型
@SupportedAnnotationTypes({"kim.hsl.router_annotation.Route"})
// 支持的 Java 版本
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class RouterProcessor extends AbstractProcessor {
    /**
     * 注解处理器中使用 Messager 对象打印日志
     */
    private Messager mMessager;
    /**
     * 用于写出生成的 Java 代码
     */
    private Filer mFiler;
    /**
     * 该函数在初始化时调用 , 相当于构造函数
     * @param processingEnvironment
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        // 获取打印日志接口
        this.mMessager = processingEnvironment.getMessager();
        mMessager.printMessage(Diagnostic.Kind.NOTE, "Messager Print Log");
        this.mFiler = processingEnvironment.getFiler();
    }
    /**
     * 该函数在注解处理器注册时自动执行, 是处理注解的核心函数
     *
     * Set<? extends TypeElement> set 参数 : 该集合表示使用了相关注解的节点的集合
     *
     * @param set
     * @param roundEnvironment
     * @return
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        for (TypeElement typeElement: set){
            mMessager.printMessage(Diagnostic.Kind.NOTE, "SupportedAnnotationTypes : " + typeElement.getQualifiedName());
            // 生成 public static void main(String[] args) 函数
            MethodSpec main = MethodSpec.methodBuilder("main")
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                    .returns(void.class)
                    .addParameter(String[].class, "args")
                    .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
                    .build();
            // 指定 public final class HelloWorld 类
            TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                    .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                    .addMethod(main)
                    .build();
            // 正式在 "com.example.helloworld" 包名下创建 HelloWorld 类
            JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
                    .build();
            try {
                javaFile.writeTo(mFiler);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }
}


修改后 , 编译时不再报上述错误 ;

image.png

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
2月前
|
Android开发
安卓SO层开发 -- 编译指定平台的SO文件
安卓SO层开发 -- 编译指定平台的SO文件
32 0
|
3月前
|
Android开发 Python
Python封装ADB获取Android设备wifi地址的方法
Python封装ADB获取Android设备wifi地址的方法
61 0
|
3月前
|
Java Shell Linux
Android——编译(一):编译make的基础知识
Android——编译(一):编译make的基础知识
69 0
|
3月前
|
Java Shell Linux
Android——编译(二):android.mk的相关知识
Android——编译(二):android.mk的相关知识
96 0
|
3月前
|
Shell Android开发
RK android13编译环境搭建与常用编译命令
RK android13编译环境搭建与常用编译命令
75 0
|
24天前
|
Android开发
Android调用相机与相册的方法2
Android调用相机与相册的方法
18 0
|
3月前
|
开发工具 Android开发
QGroundControl Qt安卓环境搭建及编译出现的问题
QGroundControl Qt安卓环境搭建及编译出现的问题
62 0
|
3月前
|
安全 Java 数据安全/隐私保护
Android和iOS应用程序加固方法详解:混淆、加壳、数据加密、动态加载和数字签名实现
Android和iOS应用程序加固方法详解:混淆、加壳、数据加密、动态加载和数字签名实现
74 0
|
3月前
|
Java Go Android开发
Android——编译(三):android.bp的相关知识
Android——编译(三):android.bp的相关知识
162 0
|
4月前
|
编解码 Ubuntu Java
Android 编译Android7.0版本源码
Android 编译Android7.0版本源码