背景
前几天看到秋百万的一篇文章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包中还有很多很有用的注解,有兴趣的可以自己一一去看一下。