译文
原文地址:http://tools.android.com/tips/non-constant-fields
前言
找到这篇文章之前,主要是因为在以前一个公司项目中,实现点击事件接口的时候有这种写法。
public void onClick(View view){
switch(view.getId()){
case R.id.xx1:
break;
case R.id.xx2:
break;
case R.id.xx3:
break;
}
}
然后我们领导说这种写法存在这问题,新api下(具体说的那个api我给忘了)可能会出现id重复的情况,不严谨。然后使用这种写法:
public void onClick(View view){
if(view.getId() == R.id.xx1){
}else if(view.getId == R.id.xx2){
}else if(view.getId == R.id.xx3){
}
}
当时我是一脸懵逼,不置可否。回头就查了一下资料。找到这篇文章。
正文
在规则的android项目中, R文件里面生命的资源常量一般像
public static final int main=0x7f030004;
然而,截止 ADT 14,在library项目中,这些常量会被声明成:
public static int main=0x7f030004;
换言之,library项目中的资源常量不在是final的。原因其实很简单,当多个library项目结合的时候,这个值无法保证唯一性。
在ADT 14之前, 所有的字段是final的, 因为这个原因, 所有的library必须拥有资源而且需要将与main project关联的java代码重新编译。这样会影响性能,导致build变慢。这样做也阻止了不含source code 的library project的分发,限制了library项目的使用范围。
由于字段不再是final的 所以也就意味着library jars可以一次编译,直接在别的项目中重用。而且编译速度变快了。
然而,像下面这种资源代码就无法在library项目编译了。
int id = view.getId();
switch (id) {
case R.id.button1:
action1();
break;
case R.id.button2:
action2();
break;
case R.id.button3:
action3();
break;
}
这是因为 switch
语句要求case 标签,就像 R.id.button1
,在编译的时候必须是常量。
(这个值会直接复制到.class文件中)。
为了解决这个问题,替换switch
语句为if-else
语句。幸好,这个操作在Eclipse是非常简单的。
仅仅是将光标放在switch
关键词上,然后点击Ctrl-1
(或者Cmd-1
on mac)。
使用之后将会将switch
代码转换为如下:
int id = view.getId();
if (id == R.id.button1) {
action1();
} else if (id == R.id.button2) {
action2();
} else if (id == R.id.button3) {
action3();
}
这是一般的UI代码,而且性能损失是微不足道的。
我们有一个探测器可以找到这种错误,而且会提供一个简单的介绍。
地址
总结
所以这么看来,因为library项目R文件中id不是final的,switch 语句需要将case 标签copy到.class 文件中。所以在library项目中switch语句中case标签不能为R.id.xxx。但是在非library中R.id.xxx 是final的,所以switch照用。
ps:
如果你的switch 语句像这样写:
switch (view.getId()) {
那么最终你将会有一个无效的if-else 链。而且每一个if检查都会请求调用view.getId()方法。所以要把view.getId()。方法放到前面来。(我擦我一直这么写的)