自定义注解-结合Eventbus

遇到一个不常用的知识点,结合案例记录下来。

先写一个最简单的自定义注解,需要在接口前加上三个注解@Documented、@Retention、@Target

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value();
}

简单使用,因为@Target是Method,所以它用来修饰方法。

public class MyTestClass {

    @MyAnnotation(value = "abc")
    public void test(String param) {

    }
}

其中@Documented是一个java.lang.annotation包中的注解,作用是将注解包含在javadoc中,至于javadoc是什么鬼?

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

第二个@Retention,同样是个注解,作用是说明保存该注解的级别,参数是一个枚举类型

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}
public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler. 
     */
    SOURCE,//注解将被编译器丢弃

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior. 
     */
    CLASS,//注解会在class文件中记录,但是在运行时会被vm丢弃

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     * 
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME  //vm将在运行期保留注释,可以通过反射读取注解信息
}

重点应用是RUNTIME类型,结合反射可以拿到方法信息,Eventbus中有用到。

第三个是@Target,作用是说明注解修饰的地方,参数类型是一个枚举类型的数组,也就是说可以多处修饰,比如可以修饰方法、变量。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}
public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,//类、接口、或enum

    /** Field declaration (includes enum constants) */
    FIELD,//域包括enum

    /** Method declaration */
    METHOD,//方法

    /** Formal parameter declaration */
    PARAMETER,//参数

    /** Constructor declaration */
    CONSTRUCTOR,//构造器的声明

    /** Local variable declaration */
    LOCAL_VARIABLE,//局部变量

    /** Annotation type declaration */
    ANNOTATION_TYPE,//注解

    /** Package declaration */
    PACKAGE,//包

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

注解@Target和@Retention的ElementType就是用的ANNOTATION_TYPE类型。自定义的注解ElementType用的是Method类型,修饰了方法。


以上是注解的基本情况,下面分析在EventBus中的运用。

在使用EventBus的时候,接收消息事件的方式一般是这样的

@Subscribe(threadMode = ThreadMode.MAIN)
    public void event(PayEvent event) {
        //dosomething
    }
其中@Subscribe是eventbus内部自定义的一个注解,看上去会感觉很熟悉。
@Documented
@Retention(RetentionPolicy.RUNTIME) //运行时保存在vm,可以使用反射获取方法信息
@Target({ElementType.METHOD})  //此注解的修饰对象是方法
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;  //参数接收一个枚举类型,有默认值

    /**
     * If true, delivers the most recent sticky event (posted with
     * {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
     */
    boolean sticky() default false;  //接收一个是否粘性的参数

    /** Subscriber priority to influence the order of event delivery.
     * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
     * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
     * delivery among subscribers with different {@link ThreadMode}s! */
    int priority() default 0;  //优先级,一般很少用
}

TheadModel是一个枚举类型,常用到Main

public enum ThreadMode {
  
    POSTING,

    MAIN,
    
    MAIN_ORDERED,
    
    BACKGROUND,

    ASYNC
}

EventBus机制不做过多解释,只看用到注解的部分,先用反射拿到监听者内部的所有方法,

Method[] methods;
try {
    // This is faster than getMethods, especially when subscribers are fat classes like Activities
    methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
    // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
    methods = findState.clazz.getMethods();
    findState.skipSuperClasses = true;
}

然后遍历方法,

for (Method method : methods) {
    int modifiers = method.getModifiers();
    if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
        //在这里拿到方法的参数对象
        Class<?>[] parameterTypes = method.getParameterTypes();
        //如果只有一个参数,很有可能是接收事件的方法,获取被@Subscribe注解修饰的方法的Subscribe对象
        if (parameterTypes.length == 1) {
            Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
            //如果不为空,说明是接收事件的方法,就取此方法中的第一个参数,即事件类型对象 public void event(PayEvent event)
            if (subscribeAnnotation != null) {
                Class<?> eventType = parameterTypes[0];
                if (findState.checkAdd(method, eventType)) {
                    //通过注解对象获取其中的参数  @Subscribe(threadMode = ThreadMode.MAIN)
                    ThreadMode threadMode = subscribeAnnotation.threadMode();
                    //封装成对象,存入list中
                    findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                            subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                }
            }
        } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
            String methodName = method.getDeclaringClass().getName() + "." + method.getName();
            throw new EventBusException("@Subscribe method " + methodName +
                    "must have exactly 1 parameter but has " + parameterTypes.length);
        }
    } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
        String methodName = method.getDeclaringClass().getName() + "." + method.getName();
        throw new EventBusException(methodName +
                " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
    }
}

借着EventBus总结对自定义注释的理解:

1、通过三个系统提供的注解@Documented、@Retention、@Target来修饰一个接口,那么这个接口就可以作为自定义注解去修饰其它的对象。

2、注解内部参数接受基本类型以及枚举等类型。

3、用@Retention来说明此注解的保存等级,如果需要运行时被vm保存,并可以使用反射获取方法信息就用RetentionPolicy.RUNTIME

4、用@Target来说明此注解的修饰目标类型,分为类、方法、域、注解、局部变量等

5、使用此自定义注解去修饰方法时,Eventbus反射思路是,先反射出监听者内所有方法,一般注解的接收事件方法会接收一个参数,所以先找到一个参数的方法,获取它的注解类,如果存在说明取对了,获取它的第一个参数作为接收事件类型;再通过注解对象拿到注解中的参数。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值