2.Android注解-编译时生成代码 APT(Annotation Processing Tool ) 实例说明

本文介绍了一个基于Java注解处理器的实现案例,展示了如何通过注解处理生成对应的代码,以简化开发流程并提高效率。该实践涉及BindView和OnClick两个注解,并详细解释了注解处理器的工作原理。

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

项目构建如下
建一个主工程,一个纯注解的anotation java工程,一个编译生成代码的compiler java工程,一个android library库。

compiler java工程不会打包入项目的,只是在编译的时候生成相关代码而已。

|—-
—app(主android项目)
—rulangtool-api(android library)
—rualngtool-annotation(java library)
—rulangtool-commpiler(java library)

工程如上(图片无法上传了,蛋疼啊 只能如此写结构图)

在app工程里面引入的是

    compile project(':rulangtool-annotation')
    compile project(':rulangtool-api')
   // apt project(':rulangtool-compiler') 
   // 刚开始我写的apt,后修改配置位annotationProcessor 
    annotationProcessor project(':rulangtool-compiler')

rulangtool-compiler只是用在编译时期生成代码,其他时间并无其他作用的。

rulangtool-annotation 是一个注解java library。

拿我们常用的android butterknife来做说明。举例BindView,Onclick(View)2个注解来说明。

rulangtool-annotation里的代码如下:
注解部分文章

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
    int value();
}
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface OnClick {
    int[] value();
}

rulangtool-commpiler来说明一下。

工程引入

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile project(':rulangtool-annotation')
    compile 'com.squareup:javapoet:1.7.0'
    compile 'com.google.auto.service:auto-service:1.0-rc2'
}

javax.lang.model.element 用于 Java 编程语言的模型元素的接口。

AnnotationMirror表示一个注释。

AnnotationValue 表示注释类型元素的值。

AnnotationValueVisitor<R,P>注释类型元素值的 visitor,使用 visitor 设计模式的变体。
Element 表示一个程序元素,比如包、类或者方法。

ElementVisitor<R,P> 程序元素的 visitor,使用 visitor 设计模式的样式。

ExecutableElement 表示某个类或接口的方法、构造方法或初始化程序(静态或实例),包括注释类型元素。

Name 字符的不可变序列。

PackageElement 表示一个包程序元素。

TypeElement 表示一个类或接口程序元素。

TypeParameterElement 表示一般类、接口、方法或构造方法元素的形式类型参数。

VariableElement 表示一个字段、enum 常量、方法或构造方法参数、局部变量或异常参数。
BindViewField


public class BindViewField {
    private VariableElement mVariableElement;
    private int mresId;

    public BindViewField(Element element) throws IllegalArgumentException{
        if (element.getKind() != ElementKind.FIELD) {
            throw new IllegalArgumentException(String.format("Only fields can be annotated with @%s",
                    BindView.class.getSimpleName()));
        }
        mVariableElement = (VariableElement) element;

        BindView bindView = mVariableElement.getAnnotation(BindView.class);
        mresId = bindView.value();
        if (mresId < 0) {
            throw new IllegalArgumentException(
                    String.format("value() in %s for field %s is not valid !", BindView.class.getSimpleName(),
                            mVariableElement.getSimpleName()));
        }
    }


    /**
     * 获取变量名称
     * @return
     */
    public Name getFieldName() {
        return mVariableElement.getSimpleName();
    }

    /**
     * 获取变量id
     * @return
     */
    public int getResId() {
        return mresId;
    }

    /**
     * 获取变量类型
     * @return
     */
    public TypeMirror getFieldType() {
        return mVariableElement.asType();
    }


}

OnClickMethod
public class OnClickMethod {

    private ExecutableElement mExecutableElement;
    private int[] resIds;
    private Name mMethodName;


    public OnClickMethod(Element element) throws IllegalArgumentException {
        if (element.getKind() != ElementKind.METHOD) {
            throw new IllegalArgumentException(
                    String.format("Only methods can be annotated with @%s",
                            OnClick.class.getSimpleName()));
        }

        mExecutableElement = (ExecutableElement) element;

        resIds = mExecutableElement.getAnnotation(OnClick.class).value();

        if (resIds == null) {
            throw new IllegalArgumentException(String.format("Must set valid ids for @%s",
                    OnClick.class.getSimpleName()));
        } else {
            for (int id : resIds) {
                if (id < 0) {
                    throw new IllegalArgumentException(String.format("Must set valid id for @%s",
                            OnClick.class.getSimpleName()));
                }
            }
        }
        mMethodName = mExecutableElement.getSimpleName();
        List<? extends VariableElement> parameters = mExecutableElement.getParameters();

        if (parameters.size() > 0) {
            throw new IllegalArgumentException(
                    String.format("The method annotated with @%s must have no parameters",
                            OnClick.class.getSimpleName()));
        }
    }

    /**
     * 获取方法名称
     * @return
     */
    public Name getMethodName() {
        return mMethodName;
    }

    /**
     * 获取id数组
     * @return
     */
    public int[] getResIds() {
        return resIds;
    }



}

同一个类里面的注解处理

AnnotatedClass

public class AnnotatedClass {

    private TypeElement mTypeElement;
    private ArrayList<BindViewField> mFields;
    private ArrayList<OnClickMethod> mMethods;
    private Elements mElements;

    public AnnotatedClass(TypeElement typeElement, Elements elements) {
        mTypeElement = typeElement;
        mElements = elements;
        mFields = new ArrayList<>();
        mMethods = new ArrayList<>();
    }

    public String getFullClassName() {
        return mTypeElement.getQualifiedName().toString();
    }

    public void addField(BindViewField field) {
        mFields.add(field);
    }

    public void addMethod(OnClickMethod method) {
        mMethods.add(method);
    }

    public JavaFile generateFile() {
        //generateMethod
        MethodSpec.Builder injectMethod = MethodSpec.methodBuilder("inject")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Override.class)
                .addParameter(TypeName.get(mTypeElement.asType()), "host", Modifier.FINAL)
                .addParameter(TypeName.OBJECT, "source")
                .addParameter(TypeUtil.PROVIDER,"provider");

        for(BindViewField field : mFields){
            // find views
            injectMethod.addStatement("host.$N = ($T)(provider.findView(source, $L))",
                    field.getFieldName(),
                    ClassName.get(field.getFieldType()), field.getResId());
        }

        for(OnClickMethod method :mMethods){
            TypeSpec listener = TypeSpec.anonymousClassBuilder("")
                    .addSuperinterface(TypeUtil.ANDROID_ON_CLICK_LISTENER)
                    .addMethod(MethodSpec.methodBuilder("onClick")
                            .addAnnotation(Override.class)
                            .addModifiers(Modifier.PUBLIC)
                            .returns(TypeName.VOID)
                            .addParameter(TypeUtil.ANDROID_VIEW, "view")
                            .addStatement("host.$N()", method.getMethodName())
                            .build())
                    .build();
            injectMethod.addStatement("View.OnClickListener listener = $L ", listener);
            for (int id : method.getResIds()) {
                // set listeners
                injectMethod.addStatement("provider.findView(source, $L).setOnClickListener(listener)", id);
            }
        }

        //generaClass
        TypeSpec injectClass = TypeSpec.classBuilder(mTypeElement.getSimpleName() + "$$RuLang")
                .addModifiers(Modifier.PUBLIC)
                .addSuperinterface(ParameterizedTypeName.get(TypeUtil.INJET, TypeName.get(mTypeElement.asType())))
                .addMethod(injectMethod.build())
                .build();

        String packgeName = mElements.getPackageOf(mTypeElement).getQualifiedName().toString();

        return JavaFile.builder(packgeName, injectClass).build();
    }

}

真正的编译器生成代码咋这个里面:

@AutoService(Processor.class)
public class ViewInjectProcesser extends AbstractProcessor {
 @Override
  public boolean process(Set<? extends TypeElement>annotations, RoundEnvironment roundEnv) {

    }
}

这句代码非常重要 TypeSpec injectClass = TypeSpec.classBuilder(mTypeElement.getSimpleName() + “

RuLang")javamTypeElement.getSimpleName()+
RuLang”.java

在rulangtool-api里面开始

  private static void inject(Object host, Object object, Provider provider) {
        String className = host.getClass().getName();
        try {
            Inject inject = injectMap.get(className);

            if (inject == null) {
            //利用反射实例话生成的对象,通过注入实例化相关字段或者关联方法。
                Class<?> aClass = Class.forName(className + "$$RuLang");
                inject = (Inject) aClass.newInstance();
                injectMap.put(className, inject);
            }
            inject.inject(host, object, provider);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值