咱们知道 Android 项目中会经过自动生成一个 R.java 类的方式来保存项目中全部资源文件的标识。在主项目中生成的 R.java 中的资源声明是一个静态常量,而在 module 中它倒是一个静态变量。这是为何呢?咱们知道在 java 中若是某个值被声明成常量(用 final 修饰),则在编译后,该常量会被直接替换成值。而在 java 语法中,注解的属性和 switch-case 中的 case 表达式,必须使用常量或者直接使用值,不然会报语法错误。下面咱们会展开讨论下为何 module 中的 R 类中声明的资源标识不是 final 的,这些又致使了哪些现象?html
好比你在主项目中建立了一个 activity_main.xml 的布局文件,则 R.java 中会自动加入一行以下静态常量。java
public static final class layout {
...
public static final int activity_main=0x7f09001b;
复制代码
此后你就能够经过 R.layout.activity_main
的方式使用该资源android
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
复制代码
咱们编译上述代码后获得 MainActivity.class
,会发现里面的静态常量被直接替换成了值。代码运行过程当中,就能够直接经过值来找到对应资源了。git
public class MainActivity extends AppCompatActivity {
public MainActivity() {
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(2131296283);
}
}
复制代码
而后咱们再在一个 module 中一样建立一个 MainActivity 和对应的资源,咱们查看该 module 下的 R.java
。github
public static final class layout {
...
public static int activity_main = 0x7f0f001c;
复制代码
你们有发现区别了吗?在 module 中添加的该资源少了 final。咱们再来看下 MainActivity.class 文件。咱们会发现此处的资源引用是使用的静态变量方式,而未直接使用资源的值。bash
public class MainActivity extends AppCompatActivity {
public MainActivity() {
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(layout.activity_main);
}
}
复制代码
Android 中,若是你在 module 中添加了一个资源,就拿这里的 activity_main.xml 举例。咱们此处假设若是在 module 中也是 final 的,那会出现什么状况?第一,该 module 编译后的代码中该资源会被替换成值;第二,当该 module 被添加到主项目中后,若是主项目中有一个一样名称的资源,那么 module 中的该资源就会被替换;第三,主项目中会从新针对该资源生成一个 ID;最终就会出现 module 中那个资源 ID 找不到了。因此呢,这也是为何 module 中的资源 ID 声明不使用 final 的缘由。ide
有关资源合并的规则,能够参考下 google 的官方文档布局
https://developer.android.com/studio/write/add-resources.html。gradle
1,这就是为何当主项目与 module 中有一样资源时,module 却会使用主项目的资源。ui
2,这也是为何咱们在 module 中没法针对资源使用 switch-case 方式的缘由。
3,这也是为何咱们没法在 module 中直接使用 butterknife,由于注解的属性须要是 final 的。固然如今 butterknife 已经提供了一个解决方案。就是利用 gradle 拷贝一份 R.java 命名成 R2.java,R2.java 里面的资源声明都是 final 的。这样就躲过了语法检查。固然使用butterknife编译后的字节码中使用的仍是R.java中的资源声明。
做者简介 彭涛(@彭涛me) 致力于让技术变得易懂且有趣 我的博客:http://pengtao.me, GitHub地址:https://github.com/CPPAlien