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傳送門

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章