Annotation Processor

本文介绍了Android中annotationProcessor与android-apt的关系,重点讲解了注解处理器的使用,包括@Retention和@Target的含义。还阐述了如何定义注解、注解处理器,并展示了使用JavaPoet生成代码的示例。最后提到了编译时注解处理器在Android开发中的应用和相关资源链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

annotationProcessor和android-apt的功能是一样的,它们是替代关系。annotationProcessor是APT工具中的一种,他是google开发的内置框架,不需要引入,可以直接在build.gradle文件中使用。android-apt是由一位开发者自己开发的apt框架,随着Android Gradle 插件 2.2 版本的发布,间接的进行了替代。

 

涉及注解相关知识:

@Retention的取值范围如下(代表注解的保留位置):

RetentionPolicy.SOURCE 表示修饰的注解只在源码中保留,编译后就被遗弃了,也就是class文件中就不存在了。
RetentionPolicy.CLASS 表示修饰的注解保留到编译后的class文件,运行时就被遗弃。比如在运行时通过反射去获取这个注解,会发现是不存在的。
RetentionPolicy.RUNTIME 表示注解一直保留到运行时。

@Target:的取值范围如下(代表注解的作用目标)

@Target(ElementType.TYPE)——接口、类、枚举、注解
@Target(ElementType.FIELD)——字段、枚举的常量
@Target(ElementType.METHOD)——方法
@Target(ElementType.PARAMETER)——方法参数
@Target(ElementType.CONSTRUCTOR) ——构造函数
@Target(ElementType.LOCAL_VARIABLE)——局部变量
@Target(ElementType.ANNOTATION_TYPE)——注解
@Target(ElementType.PACKAGE)——包

@Document:该注解可以被包含在javadoc中
@Inherited:子类可以继承父类中的该注解

三方库依赖方式:

implementation:该依赖方式所依赖的库不会传递,只会在当前module中生效。
api:该依赖方式会传递所依赖的库,当其他module依赖了该module时,可以使用该module下使用api依赖的库。

compile已过时,已被 implementation 和api 取代

当我们依赖一些第三方的库时,可能会遇到com.android.support冲突的问题,就是因为开发者使用的compile或api依赖的com.android.support包与我们本地所依赖的com.android.support包版本不一样。

一、定义注解

在工程中添加一个java library类型的module,取名annotation

Android Studio -> file -> new module -> java library

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface BindView {
    @IdRes int value();
}

备注:注解@IdRes 引用系统库,如果是jiavaLibrary需要单独添加引用

dependencies {
    implementation 'androidx.annotation:annotation:1.2.0'
}

二、定义注解处理器

新建一个module,取名为compiler,类型必须为java librar(因为有用到 javax.*下的类文件)

Android Studio -> file -> new module -> java library

新建一个类MyProcessor,这类必须继承至 AbstractProcessor

@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor {


}

备注:注解处理器创建完毕后,需要创建注册目录,如果是手动创建,目录如下:

 文件里面内容为自己处理器的全路径path

 建议使用google的 AutoService,通过@AutoService注解标记一下,就可以自动完成注册动作,避免了手动创建错误,引用如下:

dependencies {
    //google的 AutoService
    implementation 'com.google.auto.service:auto-service:1.0-rc2'
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc2'
    //自己的注解库
    implementation project(':annotation')
}

生成路径:

Processor 常用到的4个方法:

    /**
     * 初始化常用的工具类
     * @param processingEnv
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        filer = processingEnv.getFiler();//文件操作相关
        messager = processingEnv.getMessager();//日志相关
        elementUtils = processingEnv.getElementUtils();//元素相关
    }
    /**
     * 支持的注解类型
     * 也可以通过系统注解
     * @SupportedAnnotationTypes({"com.example.annotation.BindView"})
     * @return
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>();
        types.add(BindView.class.getCanonicalName());
        return types;
    }
    /**
     * 核心逻辑处理
     * @param annotations
     * @param roundEnv
     * @return
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment 
        roundEnv) {
        return true;
    }
    /**
     * 支持的java版本
     * 也可以通过系统注解代替
     * @return
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.RELEASE_7;
    }

系统注解使用如下:

@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({"com.example.annotation.BindView"})
@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor {}

如果需要调试传递参数:

defaultConfig {
        applicationId "com.example.demo0328"
        minSdkVersion 21
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [xxxkey: 'xxxxvlaue']
            }
        }
    }

注解处理器所在库获取参数:

Map<String, String> options = processingEnv.getOptions();

注解相关的api可以参考:注解与APT注解处理器技术详解 - 简书

三、新建android library:inject

public interface ViewBinder<T> {
    void bind(T target);
}


public class InjectView {

    public static void bind(Activity activity) {
        String className = activity.getClass().getName();
        try {
            // 得到我们生成的对应该Activity的ViewBinder类的Class对象
            Class<?> viewBinderClass = Class.forName(className + "$$ViewBinder");
            ViewBinder viewBinder = (ViewBinder) viewBinderClass.newInstance();
            // 绑定Activity的控件
            viewBinder.bind(activity);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

引用如下:

api project(':annotation')

接下来开始生成代码文件了。。。

代码生成

生成java文件工具有:Filer(注解处理器自带)、JavaPoet(省去手工拼接字符串麻烦)

手工生成api如下:

private void saveFile(String pkNameQ, String content) {
        String pkName = pkNameQ;
        try {
            //创建java类文件
            JavaFileObject jfo = filer.createSourceFile(pkName + ".ViewBindId", new Element[]{});
            Writer writer = jfo.openWriter();
            writer.write(writeCode(pkName, content));
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }


private String writeCode(String pkName, String content) {
        StringBuilder builder = new StringBuilder();
        builder.append("package " + pkName + ";\n\n");
        builder.append("import java.io.Closeable;\n");
        builder.append("import java.io.IOException;\n");
        builder.append("import android.util.Log;\n\n");

        builder.append("public class ViewBindId implements Closeable { \n\n");
        //生成方法 1
        builder.append("public static void setMsg(String[] args){ \n");

        builder.append("for (int i = 0; i < args.length; i++) { \n");
        builder.append("System.out.println(\"内容\"+args[i]);");
        builder.append("Log.i(\"wangsen\",\"内容:\"+args[i]);");
        builder.append("}\n");
        builder.append("System.out.println(\"" + content + "\");\n");

        builder.append("}\n");

        //生成方法 2
        builder.append("@Override\n");
        builder.append("public void close() throws IOException { \n");

        builder.append("System.out.println(\"" + content + "close\");\n");

        builder.append("}\n");

        builder.append("}");
        return builder.toString();
    }

拼接步骤麻烦,比如说换行符、导包操作等等。

javaPoet生成java文件,api如下:

 常用的api:

  • addStatement() 方法负责分号和换行
  • beginControlFlow() + endControlFlow() 需要一起使用,提供换行符和缩进。
  • addCode() 以字符串的形式添加内
  • returns 添加返回值类型
  • .constructorBuilder() 生成构造器函数
  • .addAnnotation 添加注解
  • addSuperinterface 给类添加实现的接口
  • superclass 给类添加继承的父类
  • ClassName.bestGuess(“类全名称”) 返回ClassName对象,这里的类全名称表示的类必须要存在,会自动导入相应的包
  • ClassName.get(“包名”,”类名”) 返回ClassName对象,不检查该类是否存在
  • TypeSpec.interfaceBuilder(“HelloWorld”)生成一个HelloWorld接口
  • MethodSpec.constructorBuilder() 构造器
  • addTypeVariable(TypeVariableName.get(“T”, typeClassName)) 
    会给生成的类加上泛型

占位符

  • $L代表的是字面量
  • $S for Strings
  • $N for Names(我们自己生成的方法名或者变量名等等)
  • $T for Types

具体api使用,可以参考,感谢感谢,写的挺好的:

JavaPoet使用详解_wings专栏的博客-优快云博客_javapoet

代码生成如下:

private void autoSaveFile(String pkName, String activityName, ClassName activityClass,
                              String bindViewFiledName, int resId) {
        // 创建方法
        MethodSpec main = MethodSpec.methodBuilder("bind")
                .addModifiers(Modifier.PUBLIC)//
                .returns(void.class)
                .addAnnotation(Override.class)
                .addParameter(activityClass, "target")
                .addStatement("$T.out.println($S)", System.class, "自动创建的")
                .addStatement("target.$L = target.findViewById($L)", bindViewFiledName, resId)
                .build();

        FieldSpec fieldSpec = FieldSpec.builder(int.class,"age", Modifier.PUBLIC).build();

        ClassName viewBinderClass = ClassName.get("com.example.inject", "ViewBinder");
        // 创建类
        TypeSpec ViewBinder = TypeSpec.classBuilder(activityName + "$$ViewBinder")//
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)//
                .addMethod(main)//
                .addField(String.class, "sex", Modifier.PRIVATE)//增加成员变量
                .addField(fieldSpec)
                .addSuperinterface(ParameterizedTypeName.get(viewBinderClass, activityClass))
                .build();

        //String packageName = processingEnv.getElementUtils().getPackageOf(element).getQualifiedName().toString();
        try {
            //生成java文件
            JavaFile javaFile = JavaFile.builder(pkName, ViewBinder)//
                    .addFileComment(" This codes are generated automatically. Do not modify!")//
                    .build();
            javaFile.writeTo(filer);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

调用如下:

public class MainActivity extends AppCompatActivity {

    //关键
    @BindView(R.id.tv01)
    TextView tv01;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //关键
        InjectView.bind(this);
        tv01.setText(Html.fromHtml("DD<font color='red'>注入初始化*</font>"));

       
        ViewBindId.setMsg(new String[]{"a","b","c"});
        ViewBinderAuto.setMsg(new String[]{"D","E","F"});
        try {
            new ViewBindId().close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

切记build.gradle添加相关引用:

implementation project(':inject')
//implementation project(':annotation')
annotationProcessor project(path: ':compiler')

手写拼接可以参考:EventBus,Dragger等开源库的基本原理。自动生成参考butterknife

参考:

Android关于AutoService、Javapoet讲解 - 帅气的码农 - 博客园

源码解析getCanonicalName(), getName(), getSimpleName()的不同_正在飞翔的猫的博客-优快云博客_getcanonicalname

implementation、api、compileOnly区别详解_XeonYu的博客-优快云博客_compileonly implementation

深入理解编译注解(二)annotationProcessor与android-apt_珠穆朗玛小王子的博客-优快云博客

注解与APT注解处理器技术详解 - 简书

注解入坑笔记:关于注解使用必须了解的——Annotation、AbstraceProcessor、APT_Else_Q的博客-优快云博客

秒懂Android注解处理器(Android Annotation Processor)-蒲公英云

AbstractProcessor注解处理器_廖子尧的博客-优快云博客_abstractprocessor

通过编译期生成代码方式实现的仿ButterKnife功能Demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值