1,咱们在上一篇讲到了EventBus源码及3.0版本的简单使用,知道了咱们3.0版本是使用注解方式标记事件响应方法的,这里咱们就有一个疑问了,为何在一个方法加上相似于“@Subscribe”,就可让咱们的反射找到咱们的事件响应方法。并且使用过BufferKnife、Dagger、Retrofit的同窗或常见“@XXX”这种关键字 。so,抱着弄懂一切不明真相的精神,咱们开始了这篇文章的探索。html
2,什么是注解?java
它提供了一种安全的相似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,而且供指定的工具或框架使用。安全
ok,知道你们对上面的注解定义如今是一脸懵逼,因此举一个建立的栗子吧,“@Override”关键字咱们熟悉吧,咱们建立Activity的时候,重写onCreate方法,方法上面就有这个标记,拿咱们看一下它的源码是什么框架
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
"Target"、"Rentention" 这些什么是一些什么啊 ,感受懵逼加剧。若是咱们本身写一个方法,而后在上面加上一个"@Override",看一下效果ide
看到没,直接就报错了,因此咱们在想,到底何时才能使用“@Override”关键字?能不能使用相似于“A”、“B”关键字?拥有这些标记的方法、属性、类有什么用啊?因此带着这些问题咱们先来了解简单的常理知识。工具
看到这个名字可能同窗们都有点蒙了,学过Python的同窗确定知道元数据。可是这里和咱们的元数据没有一毛钱关系,定义也很简单,"Java中提供了四个元注解,专门注解其它注解。" post
@Documented –注解是否将包含在JavaDoc中 @Retention –何时使用该注解 @Target –注解用于什么地方 @Inherited – 是否容许子类继承该注解
咦,看着上面的Retention 、Target 这不是咱们"@Override"关键字中见过吗,哦,如今知道了它们对应的是什么意思了,让咱们具体的看一下四个元注解的详细信息吧this
是否会保存到 Javadoc 文档中
这个很简单,咱们看看EventBus3.0的@Subscribe的源码spa
@Documented @Retention(RetentionPolicy.RUNTIME) @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; }
果真,这里使用了咱们的@Documented元注解了,咱们继续往下看code
是否能够被继承,默认为 false
咱们这时候有个疑问,当咱们的这个字段值为true的时候,并修饰的咱们的class类表示的什么。若是一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
这个注解表示注解的做用范围,主要有以下:
ElementType.FIELD 注解做用于变量 ElementType.METHOD 注解做用于方法 ElementType.PARAMETER 注解做用于参数 ElementType.CONSTRUCTOR 注解做用于构造方法 ElementType.LOCAL_VARIABLE 注解做用于局部变量 ElementType.PACKAGE 注解做用于包
而咱们常见的基本上适用于变量、方法,这里给你们举例一下Retrofit2.0中的"@Query"注解,这是使用的就是PARAMETER 做用于参数,源码以下:
@Documented @Target(PARAMETER) @Retention(RUNTIME) public @interface Query { /** The query parameter name. */ String value(); /** * Specifies whether the parameter {@linkplain #value() name} and value are already URL encoded. */ boolean encoded() default false; }
这个表示注解的保留方式,具体有一下三种类型
RetentionPolicy.SOURCE : 只保留在源码中,不保留在class中,同时也不加载到虚拟机中 。在编译阶段丢弃。这些注解在编译结束以后就再也不有任何意义,因此它们不会写入字节码。 RetentionPolicy.CLASS : 保留在源码中,同时也保留到class中,可是不加载到虚拟机中 。在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式 RetentionPolicy.RUNTIME : 保留到源码中,同时也保留到class中,最后加载到虚拟机中。始终不会丢弃,运行期也保留该注解,所以能够使用反射机制读取该注解的信息。咱们自定义的注解一般使用这种方式。
ok,把以上四种元数据的概念都弄懂了以后,咱们在看咱们以前看的"@Override"源码
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
表示该注解做用于方法,且只保存在源码中。
ok,再看一下咱们Java中常见的注解有哪些吧,这里只用了解了解
@Override: 表示该方法是重写父类中的方法,编译的时候会检查该方法,若是这个方法不是父类中存在的将会报错 @Deprecated: 表示该方法时已通过时的,表示该方法有风险或者有更好的替代方法 @SuppressWarnings: 表示在编译的时候忽略某种错误,如版本检查等
这里还有一个疑问,咱们的程序是怎么知道某个类中包含注解方法的呢?又是在获取注解方法中的相应属性的呢?
这里咱们使用反射来拿到class中的注解方法,主要使用这句关键代码
method.getAnnotation(XXXAnnonation.class)
3,自定义注解
咱们上面了解了一系列注解知识,如今咱们想自定义本身的注解,做用于方法,注解里面的属性有name和id,因此咱们代码以下:
package com.qianmo.eventbustest; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Created by wangjitao on 2017/4/14 0014. * E-Mail:543441727@qq.com */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface TestAnnonation { String name() default ""; int id() default 0; }
而后在Activity中标记咱们本身编写的方法,设置annonation的属性
@TestAnnonation(name = "wangjitao", id = 1) public void testMothed() { tv_message.setText(name + ":" + id); }
提供反射的方法拿到Annonation中对应的属性,保存数据
private void getAnnonationData(Class clazz) { Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { TestAnnonation testAnnonation = method.getAnnotation(TestAnnonation.class); if (testAnnonation != null) { name = testAnnonation.name(); id = testAnnonation.id(); } } }
完整代码以下:
public class MainActivity extends AppCompatActivity { private Button btn_skip; private TextView tv_message; private String name; private int id; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_skip = (Button) findViewById(R.id.btn_skip); tv_message = (TextView) findViewById(R.id.tv_message); btn_skip.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { testMothed(); } }); getAnnonationData(MainActivity.class); } @TestAnnonation(name = "wangjitao", id = 1) public void testMothed() { tv_message.setText(name + ":" + id); } private void getAnnonationData(Class clazz) { Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { TestAnnonation testAnnonation = method.getAnnotation(TestAnnonation.class); if (testAnnonation != null) { name = testAnnonation.name(); id = testAnnonation.id(); } } } }
效果以下:
ok,今天就给你们普及到这里,搞懂了以上的注解知识以后,咱们看Retrofit2.0的 @GET、@POST等还有难度吗? 小伙子们赶忙去看看来弄懂以前没搞懂的注解原理吧。。。。