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包中還有很多很有用的註解,有興趣的可以自己一一去看一下。