Android 注解和注解处理器 的使用

注解简介

注解(Annontation),Java5引入的新特性,位于java.lang.annotation包中。提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。是一种说明、配置、描述性的信息,与具体业务无关,也不会影响正常的业务逻辑。但我们可以用反射机制来进行校验、赋值等操作。

注解的定义

定义一个Annotation类型使用@interface关键字,定义一个Annotation类型与定义一个接口非常像(只是多了一个@符号)。

public @interface MyAnnotation {
    String group() default "";
}

Annotation可以是上面的简单形式,还可以包含成员变量。成员变量的一些规则:

  • Annotation的成员变量以无形参的方法形式来声明,其方法名和返回值定义了该成员变量的名字和类型,例如上面的MyAnnotation中定义了名字为group的String类型的变量 ,其默认值用default设置
  • 成员参数只能使用八种基本类型(byte、short、char、int、long、float、double、boolean)和String、Enum、Class、annotations等数据类型,及其数组。
  • 使用带有属性的Annotation时,必须为其所有定义的属性指定值(使用default的可以不用指定)
  • 定义Annotation时可以使用default关键字为属性设置默认值,使用时不为该属性指定值时会使用默认值
  • 如果Annotation中具有名为value的属性,在使用时如果只使用value属性的话,可以不写属性名直接指定值
  • 对于数组类型,当数组中只有一个元素时,可以省略大括号

元注解

元注解是专门用来注解其他注解的注解,听起来有些绕口,实际上就是专门为自定义注解提供的注解。java.lang.annotation提供了四种元注解:

  • @Documented – 注解是否将包含在JavaDoc中
  • @Retention – 什么时候使用该注解
  • @Target – 注解用于什么地方
  • @Inherited – 是否允许子类继承该注解

注解的生命周期

通过@Retention定义注解的生命周期,格式如下:

@Retention(RetentionPolicy.SOURCE)

其中RetentionPolicy的不同策略对应的生命周期如下:

  • RetentionPolicy.SOURCE : 仅存在于源代码中,编译阶段会被丢弃,不会包含于class字节码文件中。@Override, @SuppressWarnings都属于这类注解。
  • RetentionPolicy.CLASS : 默认策略,在class字节码文件中存在,在类加载的时被丢弃,运行时无法获取到。如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解
  • RetentionPolicy.RUNTIME : 始终不会丢弃,可以使用反射获得该注解的信息自定义的注解最常用的使用方式

注解的作用目标

通过@Target定义注解作用的目标,比如作用于类、属性、或方法等,默认可用于任何地方。格式如下:

@Target(ElementType.TYPE)

对应ElementType参数值适用范围如下:

  • ElementType.TYPE: 注解作用在类、接口、注解、enum
  • ElementType.CONSTRUCTOR:注解作用在构造函数
  • ElementType.FIELD: 注解作用在成员变量、对象、属性、枚举的常量
  • ElementType.LOCAL_VARIABLE: 注解作用在局部变量
  • ElementType.METHOD: 注解作用在方法
  • ElementType.PACKAGE: 注解作用在包
  • ElementType.PARAMETER: 注解作用在参数
  • ElementType.ANNOTATION_TYPE: 注解的注解,该注解可以作用在其他注解之上
  • ElementType.TYPE_PARAMETER:类型参数。
  • ElementType.TYPE_USE:类型的注解,表示这个注解可以用在所有使用Type的地方(如:泛型,类型转换等)

以模拟EventBus的实现为例子,写了一个Demo来说明注解的使用:

定义注解Subscrible,作用在方法上,有属性threadMode

package com.event.demo.eventbus;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscrible {
    ThreadMode threadMode() default ThreadMode.MAIN;
}
package com.event.demo.eventbus;

public enum ThreadMode {
    MAIN,
    BACKGROUND
}

定义一个类 SubscribleMethod来存放关于注解的相关信息

package com.event.demo.eventbus;

import java.lang.reflect.Method;

public class SubscribleMethod {

    // 回调方法中的参数类
    private Class<?> type;
    private Method method;
    private ThreadMode threadMode;

    public SubscribleMethod(Class<?> type, Method method, ThreadMode threadMode) {
        this.type = type;
        this.method = method;
        this.threadMode = threadMode;
    }

    public Class<?> getType() {
        return type;
    }

    public void setType(Class<?> type) {
        this.type = type;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public ThreadMode getThreadMode() {
        return threadMode;
    }

    public void setThreadMode(ThreadMode threadMode) {
        this.threadMode = threadMode;
    }
}

我们在自己的项目中,大部分人应该集成过EventBus,他的使用简单:

   @Override
    protected void onStart() {
        super.onStart();   //EventBus的注册
        EventBus.getDefault().register(this);
    }

    @Subscrible()
    public void onEvent(EventBean bean){  //EventBus的监听
        Log.e("=====>",bean.toString());
    }

而在早期的EventBus的实现中是用反射类实现不同组件之间的通信的,自己模拟EventBus的实现如下(代码注释很清楚):

package com.event.demo.eventbus;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class EventBus {
    private static final EventBus ourInstance = new EventBus();
    private Map<Object, List<SubscribleMethod>> mSubscriblMap = new HashMap<>();

    public static EventBus getDefault() {
        return ourInstance;
    }

    private EventBus() {
    }

    /**
     * //在Activity或者Fragment中注册  以activity或者fragment为key
     * value 是在对应的activity或者fragment中全部被@Subscribe注解的方法
     * @param obj
     */
    public void register(Object obj) {
        List<SubscribleMethod> subscribleMethods = mSubscriblMap.get(obj);
        if (subscribleMethods == null) {
            subscribleMethods = findSubscribleMethod(obj);
            mSubscriblMap.put(obj,subscribleMethods);
        }

    }

    /**
     * 传递数据  一个组件之中调用其他组件就能够接收到
     * 遍历所有注册了EventBus的组件 如果存在对应Object type类型 就直接利用反射执行该方法
     * @param type
     */
    public void post(Object type){
        Iterator<Object> iterator = mSubscriblMap.keySet().iterator();
        while (iterator.hasNext()){
            Object obj = iterator.next();
            List<SubscribleMethod> subscribleMethods = mSubscriblMap.get(obj);
            for(SubscribleMethod subscribleMethod:subscribleMethods){
                Class<?> clazz = subscribleMethod.getType();
                //class1.isAssignableFrom(class2) 判定此 Class1
                // 对象所表示的类或接口与指定的 Class2 参数所表示的类或接口是否相同,或是否是其超类或超接口。
                if(clazz.isAssignableFrom(type.getClass())){
                    Method method = subscribleMethod.getMethod();
                    try {
                        method.invoke(obj,type);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }

            }
        }
    }

    /**
     * getMethods()返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法。
     * getDeclaredMethods()对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,
     * 但不包括继承的方法。
     * @param obj
     * @return
     */
    private List<SubscribleMethod> findSubscribleMethod(Object obj){
        Class<?> clazz = obj.getClass();

        List<SubscribleMethod> list = new ArrayList<>();
        while (clazz!=null){
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                //查看method上面的Annotation
                Subscrible annotation = method.getAnnotation(Subscrible.class);
                if (annotation == null) {
                    continue;
                }
                ThreadMode threadMode = annotation.threadMode();
                Class<?>[] types = method.getParameterTypes();
                if (types.length != 1) {
                    continue;
                }
                SubscribleMethod subscribleMethod = new SubscribleMethod(types[0], method, threadMode);
                list.add(subscribleMethod);
            }
            //递归调用,看看父类 有没有使用@Subscrible注解的方法
            clazz = clazz.getSuperclass();
        }
        return list;
    }

}

APT

APT(Annotation Processing Tool)即注解处理器,是一种处理注解的工具,它对源代码文件进行检测,找出其中的Annotation,根据注解自动生成代码。使用APT的优点就是方便、简单,可以少写很多重复的代码。用过ButterKnifeDaggerEventBus等注解框架的同学就能感受到,利用这些框架可以少写很多代码,只要写一些注解就可以了 注解处理器在Android的很多开源框架中有举足轻重的地位。

还是以EventBus为例子,上面所说的是用反射的方法来处理注解,github上,EventBus的首页上也说了如下的使用方式,但是这写方式都是使用反射的方式来处理注解,耗时,复杂,侵入性强。

在greenrobot的官方网站上(地址)有讲解EventBus使用注解处理器的相关用法介绍,支持java,kotlin...很方便。使用流程大体如下:

1.在build.gradle文件中引入EventBus的相关库文件和注解处理器

implementation 'org.greenrobot:eventbus:3.1.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'

2.然后再build.gradle文件的android/defaultConfig模块下传递参数给注解处理器:

// 给注解处理器传参  com.event.demo是你的包名,后面的MyEventBusIndex是生成的类名
//MyEventBusIndex与第三步讲到的BaseApplication中要使用到的类名一致
    //eventBusIndex这个是参数名,固定写法
      javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ eventBusIndex : 'com.event.demo.MyEventBusIndex' ]
            }
        }

3.定义BaseApplication继承Application,并且初始化EventBus,此时可能会报错,提示MyEventBusIndex这个类找不到,我们需要重新build工程make moudle一下

public class BaseApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
    }
}

所有这些工作做完之后,会在你的工程目录下生成一个MyEventBusIndex文件,所在目录如下:

看一下生成的MyEventBusIndex代码如下:

package com.event.demo;

import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberMethodInfo;
import org.greenrobot.eventbus.meta.SubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberInfoIndex;

import org.greenrobot.eventbus.ThreadMode;

import java.util.HashMap;
import java.util.Map;

/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

    static {
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();

        putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEvent", com.event.demo.eventbus.EventBean.class),
        }));

    }

    private static void putIndex(SubscriberInfo info) {
        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
    }

    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }
}

代码中有一个Map对象,存放了使用@Subscribe注解了的方法:上例中可以看出在MainActivity.class中,定义了一个被@Subscribe注解标注了的onEvent方法,并且onEvent方法中的参数是EventBean对象。这个生成的代码很清晰。然后当某一个组件调用了post方法之后,就会使用这里已经缓存好的方法,类,参数等信息调用。

其实在gitHub上面EventBus3.0源码中有EventBusAnnotationProcessor这个类,其实关于上面的注解生成的类,它的一步一步写下来的。具体步骤就是,在你生成这样一个MyEventBusIndex.java文件之前,你得先自已YY 或者 写一份想生成的java文件的模板,然后根据这个模板一行一行用写文件的方式将你所需要的内容写进去,最后保存就OK

看一看其中的一个方法就明白了:

    private void createInfoIndexFile(String index) {
        BufferedWriter writer = null;
        try {
            JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(index);
            int period = index.lastIndexOf('.');
            String myPackage = period > 0 ? index.substring(0, period) : null;
            String clazz = index.substring(period + 1);
            writer = new BufferedWriter(sourceFile.openWriter());
            if (myPackage != null) {
                writer.write("package " + myPackage + ";\n\n");
            }
            writer.write("import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;\n");
            writer.write("import org.greenrobot.eventbus.meta.SubscriberMethodInfo;\n");
            writer.write("import org.greenrobot.eventbus.meta.SubscriberInfo;\n");
            writer.write("import org.greenrobot.eventbus.meta.SubscriberInfoIndex;\n\n");
            writer.write("import org.greenrobot.eventbus.ThreadMode;\n\n");
            writer.write("import java.util.HashMap;\n");
            writer.write("import java.util.Map;\n\n");
            writer.write("/** This class is generated by EventBus, do not edit. */\n");
            writer.write("public class " + clazz + " implements SubscriberInfoIndex {\n");
            writer.write("    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;\n\n");
            writer.write("    static {\n");
            writer.write("        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();\n\n");
            writeIndexLines(writer, myPackage);
            writer.write("    }\n\n");
            writer.write("    private static void putIndex(SubscriberInfo info) {\n");
            writer.write("        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);\n");
            writer.write("    }\n\n");
            writer.write("    @Override\n");
            writer.write("    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {\n");
            writer.write("        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);\n");
            writer.write("        if (info != null) {\n");
            writer.write("            return info;\n");
            writer.write("        } else {\n");
            writer.write("            return null;\n");
            writer.write("        }\n");
            writer.write("    }\n");
            writer.write("}\n");
        } catch (IOException e) {
            throw new RuntimeException("Could not write source for " + index, e);
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    //Silent
                }
            }
        

这个方法感觉里面的代码和上面MyEventBusIndex.class里面的代码差不多。其实,就是这个类生成的。只是需要一步一步write进去的。这也让我们不得不佩服这些开源大牛们。在BaseApplication中初始化之后,只要我们在Acitivity或者Fragment中调用EventBus.getDefault().register(this);之后,只要我们的Activity或者Fragment中存在符合注解处理器处理的方法,这样注解处理器就会处理,当我们发送EventBus的事件之后,将存放在MyEventBusIndex中的类,方法,等等取出来(不用反射去查找了),直接调用即可。

与APT相关的API

      大家都知道,html是一个结构体的语言,其实在这里我们也可以将java看做一个结构体语言。例如下面一个简单的实体类:

package com.event.demo.eventbus;

public class EventBean {
    private String name;
    private int age;

    public EventBean(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "EventBean{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

我们在APT的世界里面可以看成不同的结构

1.  package com.event.demo.eventbus;   package相关的信息我们可以看做是PackageElement  --包元素

2. class EventBean    class相关的信息我们可以看做 TypeElement   --类元素

3.private String name; private int age; 关于属性的信息我们可以看做VariableElement --属性元素

4. toString();  getXXX()....这些方法我们可以看做ExecutableElement--方法元素

所有的这些XXXElement都是Element的子类,每个节点中有获取自己相关信息的一些常用API。下面的API比较常用(是几个Element里面比较常用的方法,不是某一个Element的)

  1. getEnclosedElements()。返回该元素直接包含的子元素,例如包节点下的子节点就是类
  2. getEnclosingElement()。返回包含该element的父element,与上一个方法相反
  3. getKind()。返回element的类型,判断是那种类型的Element
  4. getModifiers()。回去修饰符关键字,例如public private,static,final等...
  5. getSimpleName()。获取简单名字,不带包名
  6. getQualifiedName()。获取全名,如果是类的话,包含完整的包名路径
  7. getParamters()。获取方法的参数元素,每一个元素就是一个VariableElement
  8. getReturnType()。获取方法的返回类型
  9. getConstantValue()。如果属性变量被final修饰,则可以用这个方法获得。

生成EventBusIndex.java

      上面的EventBus的注解处理器主要使用了Writer一行一行的将文件生成的方法,但是这种方法在写代码的时候,必须要非常细心,否则容易出错。其实还有一个更好使用的框架,就是javaPoet,它就是一个专门用于处理处理器相关操作的类的。javaPoet的github地址为:https://github.com/square/javapoet.

     以上面的MyEventBusIndex为例子,看看利用JavaPoet如何生成MyEventBusIndex,我这里生成EventBusIndex.java以免类名称重复

1.首先我们得有一个模板类:

public class MyEventBusIndex implements SubscriberInfoIndex {
    private static final Map<Class, SubscriberInfo> SUBSCRIBER_INDEX;

    static {
        SUBSCRIBER_INDEX = new HashMap<>();

        putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[]{
                new SubscriberMethodInfo("onEvent", EventBean.class),
        }));

    }

    private static void putIndex(SubscriberInfo info) {
        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
    }

    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }

    }
}

2.创建一个名称为annotation的module库,里面存放上面模板类需要的java类,例如SubscriberInfo.java, SubscriberInfoIndex.java ,SimpleSubscriberInfo.java , SubscriberMethodInfo.java 注意这些实体类不能 和 processor(module)在一个module里面。

3。首先当然是导相关的使用环境。新建一个名为processor的java的library,里面就是专门用作注解处理器的的相关逻辑的然后在其build.gradle文件中引入注解处理器的相关类库和javaPoet

compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
implementation project(':annotation')  //存放相关注解的module
implementation 'com.squareup:javapoet:1.11.1'
implementation project(path: ':annotation')   //上文的实体类

注意:注解处理器里面的相关处理类都是 javax.annotation.* 或者 javax.lang.*下面的。

MyProcessor注解处理类的实现如下:

/*
*
*
*
*    /**
 * //必须在 defaultConfig 节点之下
 *         //只用于apt传参
 *         javaCompileOptions {
 *             annotationProcessorOptions {
 *                 arguments = [content: 'hello apt']
 *             }
 *         }
 *         用于@SupportedOptions("content") 传参数
 * // 注解支持的注解类型,是类的全类名
 * @SupportedAnnotationTypes({"com.android.annotation.ARouter"})
 * //所支持的jdk的版本
 * @SupportedSourceVersion(SourceVersion.RELEASE_8)
 * //传递参数 name1 name2是传递参数的名称
 * @SupportedOptions({"package","desc"})
*
*
*
* */
@SupportedAnnotationTypes({"com.android.plugin.annotation.custom.Subscrible"})
@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor {
    //element的处理器
    private Elements elementUtils;
    //类信息 工具类
    private Types typeUtils;
    //日志管理器
    private Messager messager;

    //注解类生成器
    private Filer filer;
//    //获取所支持的注解类型  可以在头部用注解类来声明
//    @Override
//    public Set<String> getSupportedAnnotationTypes() {
//        return super.getSupportedAnnotationTypes();
//    }

    //jdk的版本
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.RELEASE_8;
    }



    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        elementUtils = processingEnvironment.getElementUtils();
        typeUtils = processingEnvironment.getTypeUtils();
        messager = processingEnvironment.getMessager();
        filer = processingEnvironment.getFiler();
        //获取defaultConfig 配置的 packagename  desc 的参数值
      packagename = processingEnvironment.getOptions().get("packagename");
        desc = processingEnvironment.getOptions().get("desc");
        messager.printMessage(Diagnostic.Kind.NOTE,packagename+"  "+desc);
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        try {
            if(set == null || set.isEmpty()){
                return false;
            }
            Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(Subscrible.class);
            if(elements!=null && !elements.isEmpty()){
                //====生成  private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX; 开始 ===========
                ClassName mSubscriberInfoClazz = ClassName.get("com.android.plugin.annotation.custom","SubscriberInfo");
                ClassName clazz = ClassName.get("java.lang", "Class");
                ClassName mapClazz = ClassName.get("java.util", "Map");
                TypeName mapIndexType = ParameterizedTypeName.get(mapClazz, clazz,mSubscriberInfoClazz);
                FieldSpec subscriber_index = FieldSpec.builder(mapIndexType, "SUBSCRIBER_INDEX")
                        .addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
                        .build();
                //====生成  private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX; 结束===========
                CodeBlock.Builder builder = CodeBlock.builder();
                builder.addStatement("SUBSCRIBER_INDEX = new $T<Class, $T>()", HashMap.class, SubscriberInfo.class);
                for (Element element : elements) {
                    Element enclosingElement = element.getEnclosingElement();
                    ElementKind kind = element.getKind();
                    messager.printMessage(Diagnostic.Kind.NOTE,kind.name());
                    if(kind == ElementKind.METHOD){
                        ExecutableElement executableElement = (ExecutableElement) element;
                        List<? extends VariableElement> parameters = executableElement.getParameters();
                        TypeMirror typeMirror = parameters.get(0).asType();

                        builder .addStatement("putIndex(new $T($T.class, true, new $T[]{new $T($S, $T.class)}))"
                                , SimpleSubscriberInfo.class, enclosingElement, SubscriberMethodInfo.class
                                ,SubscriberMethodInfo.class,element.getSimpleName(), typeMirror)
                                .build();
                    }
                }

                MethodSpec putIndex = MethodSpec.methodBuilder("putIndex")
                        .addModifiers(Modifier.PRIVATE, Modifier.STATIC) //加方法修饰符
                        .returns(void.class)
                        .addParameter(SubscriberInfo.class,"info")
                        .addStatement("SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);")
                        .build();

                /**
                 * SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
                 *         if (info != null) {
                 *             return info;
                 *         } else {
                 *             return null;
                 *         }
                 */

                MethodSpec getSubscriberInfo = MethodSpec.methodBuilder("getSubscriberInfo")
                        .addModifiers(Modifier.PUBLIC) //加方法修饰符
                        .addStatement("$T info = SUBSCRIBER_INDEX.get(subscriberClass)",SubscriberInfo.class)
                        .beginControlFlow("if(info!=null)")
                        .addStatement("return info")
                        .nextControlFlow("else")
                        .addStatement("return null")
                        .endControlFlow()
                        .returns(SubscriberInfo.class)
                        .addAnnotation(Override.class)
                        .addParameter(Class.class,"subscriberClass")
                        .build();

                TypeSpec myEventBusIndex = TypeSpec.classBuilder("EventBusIndex")
                        .addSuperinterface(SubscriberInfoIndex.class)
                        .addStaticBlock(builder.build())
                        .addMethod(putIndex)
                        .addMethod(getSubscriberInfo)
                        .addField(subscriber_index)
                        .addModifiers(Modifier.PUBLIC)
                        .build();


                JavaFile.builder("com.event.demo", myEventBusIndex)
                        .build()
                        .writeTo(filer);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }
}

继承 AbstractProcessor 需要实现它的几个方法,或者用注解标识:

1.自定义注解处理器    @AutoService(Processor.class) 注解是必须的,定义它就是一个注解处理器

2.重写下面方法,定义注解处理器支持的jdk版本。也可以在类上面使用注解处理@SupportedSourceVersion(SourceVersion.RELEASE_8)

,重写这个方法和在类上面家注解 ,这两种方式是等价的。

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.RELEASE_8;
    }

3.定义注解处理器所能处理的注解,这里可以自定义一个set集合,也可以使用注解处理器定义这个注解处理器能够处理的注解

// 注解支持的注解类型,是类的全类名
@SupportedAnnotationTypes({"com.android.plugin.annotation.custom.Subscrible"})
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return super.getSupportedAnnotationTypes();
    }

4.初始化方法 init

 @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        elementUtils = processingEnvironment.getElementUtils();
        typeUtils = processingEnvironment.getTypeUtils();
        messager = processingEnvironment.getMessager();
        filer = processingEnvironment.getFiler();

        //获取defaultConfig 配置的 packagename  desc 的参数值
        packagename = processingEnvironment.getOptions().get("packagename");
        desc = processingEnvironment.getOptions().get("desc");
        messager.printMessage(Diagnostic.Kind.NOTE,packagename+"  "+desc);
    }

5.process方法里面主要是注解处理的逻辑,可以根据得到注解的相关信息,然后根据注解生成对应的class文件

例如下面的代码生成一个  private static final Map<Class, SubscriberInfo> SUBSCRIBER_INDEX 的属性

//====生成  private static final Map<Class, SubscriberInfo> SUBSCRIBER_INDEX; 开始 ===========
                ClassName mSubscriberInfoClazz = ClassName.get("com.android.plugin.annotation.custom","SubscriberInfo");
                ClassName clazz = ClassName.get("java.lang", "Class");
                ClassName mapClazz = ClassName.get("java.util", "Map");

                TypeName mapIndexType = ParameterizedTypeName.get(mapClazz, clazz,mSubscriberInfoClazz);
                FieldSpec subscriber_index = FieldSpec.builder(mapIndexType, "SUBSCRIBER_INDEX")
                        .addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
                        .build();
                //====生成  private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX; 结束===========

根据模板基本实现之后,我们就可以将模板类删除掉。最后只要我们用到了com.android.plugin.annotation.custom.Subscrible 这个注解的地方,只需要编译我们的module之后,就会生成最新的对应的文件了。

生成的EventBusIndex.java类会打包的我们的APK中,在我们编译成功之后,可以像正常的类一样使用他们,调用里面的方法等。。像EventBus就是这样,然后将这些使用了注解的方法,类,参数,ThreadMode全部存储在这个index里面, 最后需要的时候直接去调用去取就OK了。不需要利用反射的技术,再去解析了。这样可以省略很多反射 解析的时间。

注意:我们的注解处理器是需要注解才能触发process方法的,因此,我们需要在我们的app  module或者其他使用到注解的module中添加  annotationProcessor project(':processor')  这样在编译我们的module的时候,就会去扫描检查我们需要处理的注解,生成对应的文件。学会使用注解,反射,注解处理器,你也可以像那些开源大牛一样写写在别人眼里看起来很牛逼的代码了。

Demo传送门

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值