1. 程式人生 > >Android 從列舉到註解最佳實踐

Android 從列舉到註解最佳實踐

背景

前幾天看到秋百萬的一篇文章Android 中的 Enum 到底佔多少記憶體?該如何用?,其實對這個東西還是很敏感的,因為最近就在Android的專案中就用到了一個列舉類。不是不知道列舉的壞處,而是列舉具有很好的約束能力,所以才選擇了它。

但是似乎在Android平臺上其約束能力體現出來的優點還不足以抵消它的缺點,恰恰其帶來的記憶體消耗犧牲更大。當然我是知道這一點的,但是並沒有對具體的數值做過測試,因為之前認為這在一個可以接受的範圍內。看了秋百萬的文章後,即使覺得不礙大事,但是在Android裡使用列舉是一個不好的習慣,畢竟Android不是Web,因此有必要重構一發程式碼。

列舉

首先來看看我這個列舉類是什麼,這是一個涉及到http介面環境的類。這個類的大致內容如下。

public enum Environment {
    /**
     * 正式環境http
     */
    RELEASE_HTTP("http://gw.release.domain.com"),
    /**
     * 預發環境http
     */
    PRE_HTTP("http://gw.pre.domain.com"),
    /**
     * 測試環境http
     */
    DAILY_HTTP("http://gw.daily.domain.com"),
    /**
     * 正式環境https
     */
RELEASE_HTTPS("https://gw.release.domain.com"), /** * 預發環境https */ PRE_HTTPS("https://gw.pre.domain.com"), /** * 測試環境https */ DAILY_HTTPS("https://gw.daily.domain.com"); /** * url */ String mEnvironmentHost; /** * @param environmentHost */
Environment(String environmentHost) { this.mEnvironmentHost = environmentHost; } //根據Environment獲得url public String getEnviromentHost() { return mEnvironmentHost; } /** * 根據url返回Environment列舉類 * @param host * @return */ public static Environment fromHost(String host) { for (Environment value : values()) { if (value.getEnviromentHost().equals(host)) { return value; } } return null; } }

之所以使用列舉類,是因為我想約束這個url為這個6個url中的其中一個,並不想讓開發者傳入這6個環境以外的其他任意一個環境。環境分為http和https,並且都具有日常,預發,線上三者,2*3=6,剛好6種。

真正使用時,直接使用列舉類限定引數型別,這樣就起到了很好的約束能力。

private Environment environment;
public void setEnvironment(Environment environment) {
    this.environment = environment;
}

其實在寫這個類的時候,我再三考慮要不要使用列舉,但是最終我用了,因為我使用常量的話,就起不到這個約束能力了。

註解

之前看到Android原始碼中有使用註解進行約束,比如資源的約束就有這麼一些註解:@ColorRes、@RawRes、@StringRes、@LayoutRes、@AnimatorRes、@AnimRes、@ArrayRes、@AttrRes、@BoolRes、@DimenRes、@DrawableRes、@IdRes等等。

其實看到過一段原始碼,是設定View的Visibility屬性的。

public void setVisibility(@Visibility int visibility) {
    setFlags(visibility, VISIBILITY_MASK);
}

當時知道可以通過註解來約束這個引數,但是沒有跟進去看這個註解到底是什麼。其實這個註解用了另一個註解@IntDef,其作用就是讓加上了@Visibility註解的引數約束為@IntDef中定義的陣列的值,這裡就是約束為了VISIBLE, INVISIBLE, GONE三個int常量

public static final int VISIBLE = 0x00000000;
public static final int INVISIBLE = 0x00000004;
public static final int GONE = 0x00000008;
@IntDef({VISIBLE, INVISIBLE, GONE})
@Retention(RetentionPolicy.SOURCE)
public @interface Visibility {}

而這個@IntDef註解是在support-annotations包中。

compile 'com.android.support:support-annotations:23.3.0'
}

除了@IntDef註解外,這個包中還有很多註解,作用其實都是為了起到一定程度的約束作用。

最佳實踐

於是,我準備用該包中的註解進行對列舉類的改造。其實用到的就一個註解,即@StringDef註解。

首先建立一個@Environment註解,在裡面定義6個字串常量,即不同的環境。

public @interface Environment {
    /**
     * 正式環境http
     */

    public static final String RELEASE_HTTP = "http://gw.release.domain.com";
    /**
     * 預發環境http
     */
    public static final String PRE_HTTP = "http://gw.pre.domain.com";
    /**
     * 測試環境http
     */
    public static final String DAILY_HTTP = "http://gw.daily.domain.com";
    /**
     * 正式環境https
     */
    public static final String RELEASE_HTTPS = "https://gw.release.domain.com";
    /**
     * 預發環境https
     */
    public static final String PRE_HTTPS = "https://gw.pre.domain.com";
    /**
     * 測試環境https
     */
    public static final String DAILY_HTTPS = "https://gw.daily.domain.com";
}

然後在該註解上加上@StringDef註解,約束為定義的6個常量

@StringDef({Environment.RELEASE_HTTP, Environment.PRE_HTTP, Environment.DAILY_HTTP, Environment.DAILY_HTTPS, Environment.PRE_HTTPS, Environment.RELEASE_HTTPS})

為了讓註解只在原始碼級別存在,我們還需要加入下面的元註解

@Retention(RetentionPolicy.SOURCE)

最後就是修改設定環境的程式碼,在其入參上加入註解限制。

private String environment;

public void setEnvironment(@Environment String environment) {
    this.environment = environment;
}

這時候,如果你使用的不是註解約束的6個環境,那麼就會報一個提示,並且是紅色的下劃線,提示你這裡需要修改。

這裡寫圖片描述

於是你必須像這麼使用

core.setEnvironment(Environment.DAILY_HTTP);
core.setEnvironment(Environment.PRE_HTTP);
core.setEnvironment(Environment.RELEASE_HTTP);
core.setEnvironment(Environment.DAILY_HTTPS);
core.setEnvironment(Environment.PRE_HTTPS);
core.setEnvironment(Environment.RELEASE_HTTPS);     

這樣以來,在一定程度上能起到約束作用。But,如果你無視了該警告,編譯仍然是可以通過的喲~,這一點需要特別注意哦。

其實這個support-annotation包中還有很多很有用的註解,有興趣的可以自己一一去看一下。