Android Support Annotations 使用詳解

ASCE1885發表於2015-07-23

在Android Support Library19.1版本中,Android工具小組引入了幾個很酷的註解型別,供開發者在工程中使用。Support Library自身也使用這些註解,這是一個好兆頭。就讓我們好好研究下。

通過gradle可以很容易的把這些註解新增到我們的工程中:

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

有三種型別的註解可供我們使用:

  • Nullness註解;
  • 資源型別註解;
  • IntDef和StringDef註解;

我們將通過程式碼例子來講解每一種型別的作用以及在工程中如何使用它們。

Nullness註解

使用@NonNull註解修飾的引數不能為null。在下面的程式碼例子中,我們有一個取值為null的name變數,它被作為引數傳遞給sayHello函式,而該函式要求這個引數是非null的String型別:

public class MainActivity extends ActionBarActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String name = null;
        sayHello(name);
    }

    void sayHello(@NonNull String s) {
        Toast.makeText(this, "Hello " + s, Toast.LENGTH_LONG).show();
    }
}

由於程式碼中引數String s使用@NonNull註解修飾,因此IDE將會以警告的形式提醒我們這個地方有問題:

深入淺出Android Support Annotations

如果我們給name賦值,例如String name = “Our Lord Duarte”,那麼警告將消失。使用@Nullable註解修飾的函式引數或者返回值可以為null。假設User類有一個名為name的變數,使用 User.getName()訪問,那麼我們可以編寫如下程式碼:

public class MainActivity extends ActionBarActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        User user = new User("Our Lord Duarte");
        Toast.makeText(this, "Hello " + getName(user), Toast.LENGTH_LONG).show();
    }
    @Nullable
    String getName(@NonNull User user) {
        return user.getName();
    }
}

因為getName函式的返回值使用@Nullable修飾,所以呼叫:

Toast.makeText(this, "Hello " + getName(user), Toast.LENGTH_LONG).show();

沒有檢查getName的返回值是否為空,將可能導致crash。

資源型別註解

是否曾經傳遞了錯誤的資源整型值給函式,還能夠愉快的得到本來想要的整型值嗎?資源型別註解可以幫助我們準確實現這一點。在下面的程式碼中,我們的sayHello函式預期接受一個字串型別的id,並使用@StringRes註解修飾:

public class MainActivity extends ActionBarActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sayHello(R.style.AppTheme);
    }

    void sayHello(@StringRes int id) {
        Toast.makeText(this, "Hello " + getString(id), Toast.LENGTH_LONG).show();
    }
}

而我們傳遞了一個樣式資源id給它,這時IDE將提示警告如下:

深入淺出Android Support Annotations

類似的,我們把警告的地方使用一個字串資源id代替警告就消失了:

sayHello(R.string.name);

IntDef和StringDef註解

我們要介紹的最後一種型別的註解是基於Intellij的“魔術常量”檢查機制(http://blog.jetbrains.com/idea/2012/02/new-magic-constant-inspection/)
(我們不需要詳細瞭解這個機制具體是如何實現的,想了解的話可以點選連結)。

很多時候,我們使用整型常量代替列舉型別(效能考慮),例如我們有一個IceCreamFlavourManager類,它具有三種模式的操 作:VANILLA,CHOCOLATE和STRAWBERRY。我們可以定義一個名為@Flavour的新註解,並使用@IntDef指定它可以接受的 值型別。

public class IceCreamFlavourManager {
    private int flavour;
    public static final int VANILLA = 0;
    public static final int CHOCOLATE = 1;
    public static final int STRAWBERRY = 2;
    @IntDef({VANILLA, CHOCOLATE, STRAWBERRY})
    public @interface Flavour {
    }
    @Flavour
    public int getFlavour() {
        return flavour;
    }
    public void setFlavour(@Flavour int flavour) {
        this.flavour = flavour;
    }
}

這時如果我們使用錯誤的整型值呼叫IceCreamFlavourManager.setFlavour時,IDE將報錯如下:

深入淺出Android Support Annotations

IDE甚至會提示我們可以使用的有效的取值:

深入淺出Android Support Annotations

我們也可以指定整型值作為標誌位,也就是說這些整型值可以使用’|’或者’&’進行與或等操作。如果我們把@Flavour定義為如下標誌位:

@IntDef(flag = true, value = {VANILLA, CHOCOLATE, STRAWBERRY})

    public @interface Flavour {

}

那麼可以如下呼叫:

iceCreamFlavourManager.setFlavour(IceCreamFlavourManager.VANILLA & IceCreamFlavourManager

                .CHOCOLATE);

@StringDef用法和@IntDef基本差不多,只不過是針對String型別而已。

關於將來計劃增加哪些新的註解型別或者這些註解的依賴以及和Intellij自身的註解如何互動等等問題,可以檢視網址:http://tools.android.com/tech-docs/support-annotations。

相關文章