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等還有難度嗎? 小夥子們趕忙去看看來弄懂以前沒搞懂的註解原理吧。。。。