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

来源:互联网 发布:登录淘宝要脸部拍摄 编辑:程序博客网 时间:2024/05/29 15:50

项目构建如下
建一个主工程,一个纯注解的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 常量、方法或构造方法参数、局部变量或异常参数。
BindViewFieldpublic 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();    }}

OnClickMethodpublic 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;    }}

同一个类里面的注解处理

AnnotatedClasspublic 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();        }    }
阅读全文
0 0
原创粉丝点击