Android 註解的一些應用以及原理

希爾瓦娜斯女神發表於2015-08-05

在這邊文章之前你首先需要對java 的註解部分有一個基本的瞭解(不需要太過的深入)。

簡單來說,註解這個東西就是用於輔助我們開發java程式碼的,註解本身無法干擾java原始碼的執行。

在android 裡面 註解主要用來幹這麼幾件事:

1.和編譯器一起給你一些提示警告資訊。

2.配合一些ide 可以更加方便快捷 安全有效的編寫java程式碼。谷歌出的support-annotations這個庫 就是主要幹這個的。

3.和反射一起 提供一些類似於spring 可配置的功能,方便簡潔(這部分有過j2ee 開發經驗的可以直接跳過了)。

 

首先來看一下官方提供的庫吧。首先在build檔案裡 增加一句話

compile 'com.android.support:support-annotations:22.2.0'

這個地方要注意,我現在是取得version22.2的版本,你們引用的時候如果有更新,android studio可能會提示你sync失敗,這個時候你要自己去谷歌官網查詢一下最新的版本號自己替換。

然後我們就可以看看這個谷歌的註解庫 為我們提供了哪些功能。

http://tools.android.com/tech-docs/support-annotations 這個是官方的文件 我也是主要根據這個來講解這個庫的使用。

首先來看 Nullness 註解 這個主要是有兩種@Nullable  和 @NonNull。

前者表示可以為空 後者表示不能為空。

我只演示後者(前者可以自己測試)定義一個函式

然後我們去呼叫他 看看。

1  String a=null;
2         sayHello(a);

 

這個時候IDE就自動會給我們警告資訊了(其實ide 的警告資訊多數也是從編譯器而來)

1 Argument 'a' might be null less... (Ctrl+F1) 
2 This inspection analyzes method control and data flow to report possible conditions that are always true or false, expressions whose value is statically proven to be constant, and situations that can lead to nullability contract violations.
3 Variables, method parameters and return values marked as @Nullable or @NotNull are treated as nullable (or not-null, respectively) and used during the analysis to check nullability contracts, e.g. report possible NullPointerException errors.
4 More complex contracts can be defined using @Contract annotation, for example:
5 @Contract("_, null -> null") — method returns null if its second argument is null @Contract("_, null -> null; _, !null -> !null") — method returns null if its second argument is null and not-null otherwise @Contract("true -> fail") — a typical assertFalse method which throws an exception if true is passed to it 
6 The inspection can be configured to use custom @Nullable
7 @NotNull annotations (by default the ones from annotations.jar will be used)

再介紹一下

Resource Type Annotations

定義一個方法

1  private String sayHello(@StringRes int resId) {
2         return "";
3     }

然後呼叫

1         sayHello(R.layout.activity_main);

這個地方這個註解的意思是你只能傳string 型別的id進去 而我們傳了layout型別的,如果你不加那個註解的話 ide和編譯器是沒有反應的,到執行期才會有錯誤。

但是你加了以後就會發現

IDE也是直接報錯的!

 

再比如這樣一段程式碼

 1  class TestAnoataion {
 2 
 3         public void testAnoation() {
 4             Log.v("main", "test anoation");
 5         }
 6     }
 7 
 8     class TestAnoataion2 extends TestAnoataion
 9     {
10         public void testAnoation()
11         {
12 
13         }
14     }

假使我們父類的設計的時候本意是 如果你要重寫testAnoation這個方法一定得呼叫父類的testAnation這個方法。就跟我們activity的oncreate方法一樣。

哪如果子類沒有寫super.testAnoation的話 你根本就不知道 就很容易報錯。但是如果你加了註解。那麼ide就會很明確的提示你錯誤

是不是很強大?當你呼叫了super語句以後 一切就和諧了!

那這個support庫 就暫時介紹到這裡,個人建議大家有時間一定要自己過一遍一個官方文件。

這裡定義的註解 對提高大家的程式碼質量 會非常非常有幫助。一定要掌握學會。

 

最後再上一個小例子吧,很多android 開發者都喜歡依賴注入的一些開源庫,來繫結你的控制元件id。

這樣寫法不但優雅而且好用。

Butterknife

這個開源庫大家一定很多人都用過,我就寫一個小demo 來演示一下這些庫的原理,其實還蠻簡單的。

需要注意的是 要理解這個小demo 需要你除了知道註解的基礎知識以外還需要有反射的基礎知識。

關於反射我很久之前也寫過一個教程

http://www.cnblogs.com/punkisnotdead/p/3384464.html

不會的可以趕緊補一下。

首先定義一個InjectView註解

 1 package com.example.administrator.testfab;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 
 8 /**
 9  * Created by Administrator on 2015/8/5.
10  */
11 
12 @Target(ElementType.FIELD)
13 @Retention(RetentionPolicy.RUNTIME)
14 public @interface InjectView {
15     //id就表示哪些控制元件,-1就表示取不到時候的預設值
16     int id() default -1;
17 }

然後定義一個直譯器

 1 package com.example.administrator.testfab;
 2 
 3 import android.app.Activity;
 4 import android.view.View;
 5 
 6 import java.lang.reflect.Field;
 7 
 8 /**
 9  * Created by Administrator on 2015/8/5.
10  */
11 public class InjectViewParser {
12 
13     public static void inject(Object object) {
14 
15         try {
16             parse(object);
17         } catch (Exception e) {
18             e.printStackTrace();
19         }
20     }
21 
22     public static void parse(Object object) throws Exception {
23         final Class<?> clazz = object.getClass();
24         View view = null;
25         Field[] fields = clazz.getDeclaredFields();
26         for (Field field : fields) {
27             if (field.isAnnotationPresent(InjectView.class)) {
28                 InjectView injectView = field.getAnnotation(InjectView.class);
29                 int id = injectView.id();
30                 if (id < 0) {
31                     throw new Exception("id must not be null");
32                 } else {
33                     field.setAccessible(true);
34                     if (object instanceof View) {
35                         view = ((View) object).findViewById(id);
36                     } else if (object instanceof Activity) {
37                         view = ((Activity) object).findViewById(id);
38                     }
39                     field.set(object, view);
40                 }
41 
42             }
43 
44         }
45 
46     }
47 }

最後在actity裡使用即可

 

 1 package com.example.administrator.testfab;
 2 
 3 import android.os.AsyncTask;
 4 import android.os.Bundle;
 5 import android.support.annotation.CallSuper;
 6 import android.support.annotation.NonNull;
 7 import android.support.annotation.Nullable;
 8 import android.support.annotation.StringRes;
 9 import android.support.v7.app.AppCompatActivity;
10 import android.util.Log;
11 import android.view.Menu;
12 import android.view.MenuItem;
13 import android.widget.Button;
14 
15 public class MainActivity extends AppCompatActivity {
16 
17     @InjectView(id = R.id.bt)
18     private Button bt;
19 
20 
21     @Override
22     protected void onCreate(Bundle savedInstanceState) {
23         super.onCreate(savedInstanceState);
24         setContentView(R.layout.activity_main);
25         //開始注入
26         InjectViewParser.inject(this);
27         //這個主要是測試注入id 成功沒有 成功了就不會報錯~
28         bt.setText("inject done");
29 
30 
31     }
32 
33 
34     @Override
35     public boolean onCreateOptionsMenu(Menu menu) {
36         // Inflate the menu; this adds items to the action bar if it is present.
37         getMenuInflater().inflate(R.menu.menu_main, menu);
38         return true;
39     }
40 
41     @Override
42     public boolean onOptionsItemSelected(MenuItem item) {
43         // Handle action bar item clicks here. The action bar will
44         // automatically handle clicks on the Home/Up button, so long
45         // as you specify a parent activity in AndroidManifest.xml.
46         int id = item.getItemId();
47 
48         //noinspection SimplifiableIfStatement
49         if (id == R.id.action_settings) {
50             return true;
51         }
52 
53         return super.onOptionsItemSelected(item);
54     }
55 
56 
57 
58 }

 

 

 

 

 


  

相關文章