合理使用Android提供的Annotation來提高代碼的質量


概述

        Java語言提供了Annotation的機制,讓描述性的元數據能夠和代碼共存。通常我們可以利用Annotation,來做一些標志性的說明。然而Annotation必須和相應的解析工具一起才能工作。合理的運用Annotation,會帶來一些額外的效果。

 

 

本文不討論Annotation的基礎語法以及基礎使用方法。

 

 

Android用Annotation干了什么?

        Java這種強類型的語言,在編寫代碼的時候,編輯器就會通過語法檢查,來阻止一些錯誤的發生。相比於一些弱類型的語言,更安全。Annotation作為元數據而存在,結合上運行在編輯期的解析工具,可以做到更細致的代碼檢查,可以說是一種對語法檢查的擴充吧。

        Android就充分利用了這一點,Android在提供了大量的Annotation的同時,也在ADT中提供了操作這些Annotation的工具,主要體現在代碼檢查階段。下面我們會詳細看一下Android提供的Annotation。這些Annotation不僅可以讓代碼在編寫的時候,少出一些問題,而且可以對一個類、方法等做詳細的說明。這些Annotation也可以指導使用API的開發人員,正確的使用API。

 

 

 

Android是如何運用Annotation來進行代碼檢查的?

        Android提供了幾大類的Annotation,用來配合其相應的解析工具,來對代碼做更嚴格的檢查。

 

1、強制方法調用類型。

        作用:指示所有Override該方法的方法,都需要調用調用該方法。

        Annotations:@CallSuper

        使用場景:你設計了一個框架,里面有一些基礎方法,各子類需要覆寫該方法,但是一定要調用父類的方法。這時候可以使用這個Annotation來強制子類調用父類的方法。

        示例代碼:

    //父類
    @CallSuper
    protected void fun() {
        System.out.println("parent fun()");
    }

    //子類
    @Override
    protected void fun() {
        super.fun();
    }

 

        如果不調用 super.fun(),則會出現提示:Overriding method should call super.fun。

       

 

2、指定固定常量型。

        作用:指示一個邏輯類型,並且這個邏輯類型的值只能取固定的常量。

        Annotations:@IntDef

                                @StringDef

        使用場景:作為一個特定類型的有限個取值,可以使用Enum或者使用常量,但是Enum可以有類型檢查,而常量沒有。眾所周知,因為內存占用的問題,Android不推薦使用Enum,那就只能使用常量,但是常量不能做類型檢查,所以Android就提供了這樣一種方法,來解決此問題,讓指定的屬性值只能取定義好的常量。

        示例代碼:

    //定義常量
     public static final int INT_1 = 1;
    public static final int INT_2 = 2;

    //指定該Annotation描述的對象,只能使用這兩個常量
     @IntDef({INT_1, INT_2})
    public @interface MyType {
    }

    //如果一個方法只想接收這兩個常量(常量名稱,而不是常量的值)作為參數,那么可以這樣使用
     private void f (@MyType int time) {
    }


        如果調用f()時,不傳遞這兩個常量,比如f(2)。那么就會報錯:Must be one of: INT_1, INT_2。


 

 

3、資源對象的值類型指定型。

        作用:指示所描述的參數、字段、返回值必須是指定的資源類型的引用。

        Annotations:@AnimatorRes
                               @AnimRes
                               @AnyRes
                               @ArrayRes
                               @AttrRes
                               @BoolRes
                               @ColorRes
                               @DimenRes
                               @DrawableRes
                               @FractionRes
                               @IdRes
                               @IntegerRes
                               @InterpolatorRes
                               @LayoutRes
                               @MenuRes
                               @PluralsRes
                               @RawRes
                               @StringRes
                               @StyleableRes
                               @StyleRes
                               @TransitionRes
                               @XmlRes

        使用場景:如果你有一個方法,需要一個 R.id 的值作為參數,或者一個方法,希望返回值是 R.anim 類型的引用,那么這個時候就可以使用這些Annotation。

        示例代碼:

    //希望接收一個id作為參數
     private void f (@IdRes int id) {
    }

    private void call() {
       //但是實際上不小心傳遞了一個layout進去。
        f(R.layout.day_view);
    }


        那么這個時候就會報錯:Expected resource of type id。

 

 

4、取值范圍型。

         作用:指示一個int、float或者double類型的值,合理的取值范圍。

         Annotations:@FloatRange

                                 @IntRange

        使用場景:比如有一個方法,接收一個int型的透明度參數,那么該方法就會希望傳進來的值應該是0~255,這時就可以使用該Annotation。

        示例代碼:

    //希望接收一個[0, 255]的值作為透明度
     private void setAlpha (@IntRange(from = 0, to = 255) int alpha) {
    }

    private void call() {
        //但是實際上不小心傳遞了一個300進去。
        setAlpha(300);
    }


        那么這個時候就會報錯:Value must be ≥ 0 and ≤ 255 (was 300)。

 

 

5、線程標識型。

        作用:指示某個對象、構造器或者方法,應該在指定的類型的線程運行。

        Annotations:@BinderThread

                                @MainThread

                                @UiThread

                                @WorkerThread

        使用場景:如果有一個方法,比較耗時,所以它應該放在工作線程執行。這時候可以用@WorkerThread來標注該方法,當在主線程調用該方法的時候,就會報錯。

        示例代碼:

    //該方法比較耗時,所以放在子線程執行
     @WorkerThread
    private void shouldRunOnWorkerThread() {
    }

    @MainThread
    private void runOnMainThread() {
        //在主線程調用該方法,會報錯。
         shouldRunOnWorkerThread();
    }

        那么這個時候就會報錯:Method shouldRunOnWorkerThread must be called from the worker thread, currently inferred thread is main。

        像 Activity 的onCreate方法,都是被標注為主線程執行的。

    @MainThread
    @CallSuper
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        .......................;
    }



 

6、空指針指示型。

        作用:指示某個參數、返回值等是否可以為空。

        Annotations:@NonNull

                                @Nullable

         使用場景:比如一個方法,可能返回一個null,可以使用@Nullable來標注,那么使用該方法的地方,一般都要做null判斷。

 

 

7、混淆指示類型。

        作用:指示被描述的對象,在編譯的時候不要被混淆。

        Annotations:@Keep

        使用場景:當一個方法、屬性、對象等,需要被反射使用時,比如通過反射來實現工廠。這時候可以使用該Annotation來標記,這樣在編譯的時候,就不會被混淆(如果開了混淆的話)。

        示例代碼:

//抽象產品,避免被混淆
     @Keep
    class Product {}

    //產品A,避免被混淆
     @Keep
    class ProductA extends Product{}

    //產品B,避免被混淆
     @Keep
    class ProductB extends Product{}

    private Product getProduct(String productName) {
        //使用反射創建具體的Product
        return ...;
    }

 

 

8、參數長度限定類型。

        作用:指示被描述的對象應該有明確的大小。

        Annotations:@Size

        使用場景:當一個參數,需要最小的大小時,使用該Annotation,比如,一個數組最少有2個元素、一個String最低長度為3等。

        示例代碼:

    //要求param的長度最小為2
    private void minSize(@Size(min = 2) String param) {
    }

    private void callMinSize() {
        //傳遞長度只有1的"s"時,會報錯
          minSize("s");
    }


        那么這個時候就會報錯:Length must be at least 2 (was 1)。

 

 

 

9、其他類型。

        作用:指示一個int值是顏色類型的值(AARRGGBB)。

        Annotations:@ColorInt

        示例代碼:

    //標明該常量代表顏色值
     @ColorInt public static final int BLACK = 0xFF000000;
    //標明該方法希望接收一個代表顏色值的參數
     public setColor(@ColorInt int color) {
    }


 

 

       

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM