Android進階Support Annotation Library 使用詳解

Support Annotation Library是從Android Support Library 19.1 開始引入的一個全新的函數包,它包含一系列有用的元註解,用來幫助開發者在編譯期間發現可能存在的Bug。Support Library本身也使用Annotation Library 提供的註解來完善自身的代碼質量,Android Studio 提供可視化的交互以便開發者發現問題。

Android Support Library 發展到現在已經不止是一個jar包了,而是拆分成多個獨立的Jar包,例如support-v4、support-v7、gridlayout-v7、design、cardview-v7等等。而Annotation Libary 也是其中之一,默認情況下是不會包含在工程中的,如果我們的SDK已經安裝了android Support Repository,那麼我們打開Project Structure 對話框,並選中一個Module,選中Dependencies選項,點擊“+”按鈕,在彈出的Choose Library Dependency 對話框中輕鬆找到Annotation Library。

這裏寫圖片描述

點擊添加後,在Module的build.gradle文件中會新增加Annotation函數庫的依賴如下:

compile 'com.android.support:support-annotations:26.0.0-alpha1'

下面我們按照類型進行一一介紹。

1. Nullness 註解

此類型包含如下內容:

  • @Nullable作用於函數參數或返回值,標記參數或返回值可以爲空。
  • @NonNull作用於函數參數或返回值,標記參數或返回值不能爲爲空。

如果在函數參數或返回值使用了上述註解,而又出現違反該註解的代碼時,Android Studio 會給出提示,同時使用Android Lint進行靜態代碼掃描,也會顯示出錯提示。

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        test(null);
    }
    private void test(@NonNull String str) {
        Log.e(TAG,str);
    }

我們用Android Lint掃描這個文件的話,在掃描結果中會出現如下錯誤:

這裏寫圖片描述

2. 資源類型註解

我們知道,資源是以int整型表示,並保存在R.Java文件中。這就意味着在一個需要Layout資源值函數傳入String字符串,在編譯時不會報錯,只有在運行時纔會報錯,爲了防止這種情況的出現,可以使用資源類型註解。

資源類型的註解作用於函數參數、返回值及類的變量,每種資源類型對應一種註解。

  • AnimatorRes:標記整型值是android.R.animation類型。

  • AnimRes:標記整型是android.R.anim類型。

  • AnyRes:標記整型是任何一種資源類型,如果確切知道表示的是哪一個具體資源的話,建議顯式指定。

  • ArrayRes:標記整型是android.R.array類型。

  • AttrRes:標記整型是android.R.attr類型。

  • BoolRes:標記整型是布爾類型。

  • ColorRes:標記整型是android.R.color類型。

  • DrawableRes:標記整型是android.R.drawable類型。

  • FranctionRes:標記整型值是fraction類型,這個比較少見,這種類型資源常見於Animation
    Xml中,比如50%,表示佔parent的50%

  • IdRes:標記整型是android.R.id類型。

  • IntegerRes:標記整型是android.R.integer類型。

  • InterpolatorRes:標記整型是android.R.interpolator類型,插值器,在Animation
    Xml中使用較多。

  • LayoutRes:標記整型是android.R.layout類型。

  • MenuRes:標記整型是android.R.menu類型。

  • RawRes:標記整型是android.R.raw類型。

  • StringRes:標記整型是android.R.string類型。

  • StyleableRes:標記整型是android.R.styleable類型。

  • StyleRes:標記整型是android.R.style類型。

  • XmlRes:標記整型是android.R.xml類型。

看一下例子。這裏傳入字符串若是不加@LayoutRes,是不會報錯,

若是加了就會報錯。提前知道錯誤在哪。這樣就會少很多麻煩。舉個例子,在AppCompatAcitivity的setContentView函數使用@LayoutRes標記它的參數。

@Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }

3. 類型定義註解

在Android開發中,整型值不止經常用來代表資源引用值,而且經常用來代替枚舉值。@IntDef註解用來創建一個整型類型定義的新註解,我們可以使用這個新註解來標記自己編譯的API。先看看@IntDef的源碼。

@Retention(SOURCE)  
@Target({ANNOTATION_TYPE})  
public @interface IntDef {  
    /** Defines the allowed constants for this element */  
    long[] value() default {};  

    /** Defines whether the constants can be used as a flag, or just as an enum (the default) */  
    boolean flag() default false;  
}

這裏面可以定義一個布爾值,還可以定義多個long類型的值。那麼就以long數組舉例。

public abstract class AnnotationTest {  
    public static final int TEST_1 = 0;  
    public static final int TEST_2 = 1;  
    public static final int TEST_3 = 2;  
    @Retention(RetentionPolicy.SOURCE)  
    @IntDef({TEST_1,TEST_2,TEST_3})  
    public @interface TestAnnotation{}  

    @TestAnnotation  
    public abstract int getTestAnnotation();  

    public abstract void setTestAnnotation(@TestAnnotation int testAnnotation);  
}

這裏給TestAnnotation註解加上了@IntDef,這樣在使用TestAnnotation的時候必須傳入指定參數,若是非法在編譯時就會報異常。

使用就非常簡單了,找個類繼承抽象類,實現方法。在調用的時候,只能傳入指定的TEST_1,TEST_2,TEST_3。

這樣就可以自定義資源類型註解,非常方便。

4. 線程註解

Android應用開發過程中,經常會涉及到多種線程的使用,界面相關操作必須在主線程,而耗時操作如文件下載等必須在後臺線程中,

線程註解相關有四種。

  • @UiThread:標記運行在UI線程,一個應用只有一個UI線程,多數是用於View的標註。(這裏先前有錯誤,感謝樓下評論朋友提醒)

  • @MainThread:標記運行在主線程,一個應用只有一個主線程,主線程也是@UiThread線程。通常情況下,我們使用@MainThread

來註解生命週期相關函數,使用@UiThread來註解視圖相關函數,一般情況下@MianThread和@UiThraed是可以互換的。

  • @WorkerThread:標記運行在後臺運行線程。

  • @BinderThread:標記運行在Binder線程。

一個典型的例子就是AsyncTask的源碼,我們截取部分代碼如下。

    @MainThread
    protected void onPreExecute() {
    }
    @MainThread
    protected void onPostExecute(Result result) {
    }
    @MainThread
    protected void onProgressUpdate(Progress... values) {
    }

5. RGB顏色值註解

在資源類型註解中我們使用@ColorRes來標記參數類型需要傳入顏色類型的id,而使用@ColorInt註解是標記參數類型需要傳入RGB或者ARGB顏色值的整型值,在TextView的源碼中可以找到@ColorInt的例子。

@android.view.RemotableViewMethod
public void setTextColor(@ColorInt int color) {
        mTextColor = ColorStateList.valueOf(color);
        updateTextColors();
    }

6. 值範圍註解

當函數參數的取值在一定範圍時,可以使用註解來防止調用者傳入錯誤的參數,主要註解有三種註解。

  • @Size:對於類似數組、集合和字符串之類的參數,我們可以使用@Size註解來表示這些參數的大小。用法:

    • @Size(min=1)//可以表示集合不可以爲空

    • @Size(max=23)//可以表示字符串最大字符個數爲23

    • @Size(2)//表示數組元素個數爲2個

    • @Size(multiple=2)//可以表示數組大小是2的倍數

  • @IntRange:參數類型是int或者long,用法如下

public void setAlpha(@IntRange(from=0,to=255) int alpha){...}
  • @FloatRange:參數類型是float或者double,用法如下。
public void setAlpha(@FloatRange(from=0.0,to=1.0) float alpha){...}

7. 權限註解

Android應用在使用某些系統功能時,需要在AndroidManifest,xml中聲明權限,否則在運行時就會提示缺失對應的權限,爲了在編譯時及時發現權限的缺失,我們可以使用@RequiresPermission註解。

  • 如果函數調用需要聲明一個權限,語句如下:
@RequiresPermission(Manifest.permission.SET_WALLPAPER)
public abstract void setWallpaper(Bitmap bitmap) throws IOException;
  • 如果需要一個集合至少一個權限,語句如下:
@RequiresPermission(anyOf= {
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION})
public abstract Location getLastKnownLocation(String Provider);
  • 如果同時需要多個權限,語句如下。
@RequiresPermission(allOf = {
Manifest.permission.READ_HISTORY_BOOKMARKS,
Manifest.permission.WRITE_HISTORY_BOOKMARKS})
public static final void updateVisitedHistory(ContentResolver cr,String url,boolean real);
  • 對於Intent調用所需權限,可以在Intent的ACTION字符串定義處添加註解。語句如下:
@RequiresPermission(android.Manifest.permission.BLUETOOTH)
public static final String ACTION_REQUEST_DISCOVERRAVLE = "android.bluetooth.adapter.REQUEST_DISCOVERRAVLE";
  • 對於ContentProvider所需權限,可能有讀和寫兩個操作。對應不同的權限。
@RequiresPermission.Read(@RequestPermission(READ_HISTORY_BOOLMARKS))
@RequiresPermission.Write(@RequestPermission(WRITE_HISTORY_BOOLMARKS))
public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");

8. 重寫函數註解

如果API允許重寫某個函數,但是要求在重寫該函數時需要調用super父類的函數,否則代碼邏輯可能會出錯,那麼可以加註解@CallSuper來提示開發者。

@CallSuper
protected void onCreate(@Nullable Bundle saveInstanceState);

9. 返回值註解

如果我們編寫的函數需要調用對返回值做某種處理,那麼可以使用@CheckResult註解來提示開發者。當然我們沒有必要對每個非空返回值的函數都添加這個註解,該註解的主要目的是讓調用者在使用API時不至於懷疑該函數是否會產生副作用。看下Context類中checkPermission中的源碼:

    @CheckResult(suggest="#enforcePermission(String,int,int,String)")
    @PackageManager.PermissionResult
    public abstract int checkPermission(@NonNull String permission, int pid, int uid);

    @CheckResult(suggest="#enforceCallingPermission(String,String)")
    @PackageManager.PermissionResult
    public abstract int checkCallingPermission(@NonNull String permission);

如果調用者沒有檢查這兩個函數的返回值,那麼就會警告,警告信息中包含suggest屬性中的內容。

10. @VisibleForTesting註解

單元測試中可能需要訪問到一些不可見的類、函數或者變量,這時可以使用@VisibleForTesting註解來使其對測試可見。

11. @Keep註解

@keep是用來標記在Proguard混淆過程中不需要混淆的類或者方法。在混淆時一些不需要混淆的會使用

-keep class com.foo.bar{public static method>}

有了@Keep之後,就可以在編碼時標註出一些不需要混淆的類或者方法

public class AnnotationDemo {
    @Keep
    public void doSomething(){
    // ...
    }
// ...
}

12. @SuppressWarnings忽略警告

java.lang.SuppressWarnings是J2SE 5.0中標準的Annotation之一。可以標註在類、字段、方法、參數、構造方法,以及局部變量上。作用:告訴編譯器忽略指定的警告,不用在編譯完成後出現警告信息。

注意:SuppressWarnings是java.lang包下的,之前說的都是android.support.annotation包下的

使用:

  • @SuppressWarnings(“”)
  • @SuppressWarnings({})
  • @SuppressWarnings(value={})

示例:

  • @SuppressWarnings(“unchecked”)

    告訴編譯器忽略 unchecked 警告信息,如使用List,ArrayList等未進行參數化產生的警告信息。

  • @SuppressWarnings(“serial”)

    如果編譯器出現這樣的警告信息:The serializable class WmailCalendar does not declare a static final serialVersionUID field of type long

    使用這個註釋將警告信息去掉。

  • @SuppressWarnings(“deprecation”)

    如果使用了使用@Deprecated註釋的方法,編譯器將出現警告信息。
    使用這個註釋將警告信息去掉。

  • @SuppressWarnings(“unchecked”, “deprecation”)

    告訴編譯器同時忽略unchecked和deprecation的警告信息。

  • @SuppressWarnings(value={“unchecked”, “deprecation”})

    等同於@SuppressWarnings(“unchecked”, “deprecation”)

1、抑制單類型警告

@SuppressWarnings("unchecked")
public void addItems(String item){
  @SuppressWarnings("rawtypes")
   List items = new ArrayList();
   items.add(item);
}

2、抑制多類型警告

@SuppressWarnings(value={"unchecked", "rawtypes"})
public void addItems(String item){
   List items = new ArrayList();
   items.add(item);
}

3、抑制全部警告

@SuppressWarnings("all")
public void addItems(String item){
   List items = new ArrayList();
   items.add(item);
}

註解目標  
                              
通過 @SuppressWarnings 的源碼可知,其註解目標爲類、字段、函數、函數入參、構造函數和函數的局部變量。而家建議註解應聲明在最接近警告發生的位置。

到這裏基本註解都已經說完了,最後說明一下,如果函數庫中使用Annotation Library,並使用Gradle生成aar壓縮包,那麼在編譯時Android Gradle插件會取出這些註解信息並打包在aar文件中,以便函數庫的調用者正常使用我們的註解信息。aar文件中的annotations.zip文件就是抽取出來的註解信息。

參考:
@SuppressWarnings忽略警告
《Android高級進階》

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