Android库项目中的资源ID冲突
1、前言
Android Studio对模块化开发提供的一个很有用的功能就是可以在主项目下新建库项目(Module),但是在使用库项目时却有一个问题就是资源ID冲突,因为编译时SDK会自动帮我们处理这个问题,所以一般我们不会察觉到,但是在某些情况下,我们需要意识到这个问题的存在。
比如,在新建的库项目中使用如下代码:
public void onButtonClick(View view) {
switch (view.getId()) {
case R.id.button_1:
break;
case R.id.button_2;
break;
}
}
IDE会提示:
Resource IDs cannot be used in a switch statement in Android library modules less.
Validates using resource IDs in a switch statement in Android library module. Resource IDs are non final in the library projects since SDK tools r14, means that the library code cannot treat these IDs as constants.
再比如,我们在库项目中以如下方式使用ButterKnife,编译时就会报错。
@OnClick(R.id.button_1)
public void onButtonClick(View view) { }
2、分析
无论是 switch 语句还是注解,都有一个要求就是使用的值必须是常量。在主项目中, R类中的成员变量都被 static final 修饰,而在库项目中仅被 static 修饰。
// 库项目中生成的R类:
public final class R {
public static final class id {
public static int button_1 = 0x7f0c0001;
}
} // 主项目中生成的R类:
public final class R {
public static final class id {
public static final int text_1 = 2131165184;
}
}
为什么库项目中生成的资源ID没有被 final 修饰呢?官方解释如下:
Non-constant Fields in Case Labels
当多个库项目进行合并时,不同项目中的资源ID可能会重复。在ADT 14之前,无论是主项目还是库项目,资源ID统一被定义为 final 类型的静态变量。这样照成的结果就是主项目进行编译时一旦发现资源ID冲突,库项目中对应的资源文件以及引用资源文件的代码都需要重新编译。
如果代码中使用了被 static final 修饰的变量,那这个变量实际上就是一个常量,编译时会直接使用它的值进行替换。在编译时,如果库项目与主项目的资源ID发生了重复,资源被分配了新的ID后库项目之前编译过的代码也就失效了。
那么当库项目R类中的变量仅被 static 修饰后会起到什么作用呢,我们可以看一下编译后的字节码再反编译后的样子。
// 主项目中的Activity:
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 源代码:setContentView(R.layout.activity_main);
this.setContentView(2131296283);
}
}
// 库项目中的Activity:
public LibActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_lib);
}
}
主项目R类中的资源ID被 static final 修饰,编译时直接被替换成了对应的常量。库项目R类中的资源ID仅被 static 修饰,所以保留了变量。这样当资源ID发送冲突时,主项目R类不变,修改库项目R类中的变量,库项目已经编译过的代码仍有效。
3、ButterKnife中的R2类
既然库项目中的资源ID不可以定义为常量,那如何在库文项目使用ButterKnife呢,作者提供了R2类供我使用。
@OnClick({R2.id.button_1, R2.id.button_2})
public void onButtonClick(View view) {
int id = view.getId();
if (id == R.id.button_1) {
// ...
} else if (id == R.id.button_2) {
// ...
}
}
没错在注解中使用R2类,但是在代码里还是需要使用R类,因为R类中的ID不是常量,所以只能使用 if 语句进行判断。
先来看一下ButterKnife为我们生成的R2类与R类有什么不同:
// 库项目中的R类:
public final class R {
public static final class id {
public static int button_1 = 0x7f0c0001;
}
}
// 库项目中ButterKnife为我们生成的R2类:
public final class R2 {
public static final class id {
public static final int button_1 = 0x7f0c0001;
}
}
ButterKnife做的工作很简单,仅仅是把R类中的变量搬到了R2类里,然后给所有的变量都加上了 final 。根据前面所说,当项目整体编译时,库项目的资源ID一旦与主项目的资源ID发送冲突,库项目的资源会被重新分配ID导致其R类被修改。显然这个过程并不涉及R2类,R2类中保留的仍然是过时的ID。但是ButterKnife提供的注解的作用是什么,它们并不是为了提供运行时信息,而是为了在编译时生成代码。
public class LibActivity_ViewBinding implements Unbinder { private LibActivity target;
private View view_button_1;
private View view_button_2; @UiThread
public LibActivity_ViewBinding(final LibActivity target, View source) {
this.target = target;
View view = Utils.findRequiredView(source, R.id.button_1, "method 'onButtonClick'");
this.view_button_1 = view;
//view.setOnClickListener....
view = Utils.findRequiredView(source, R.id.button_2, "method 'onButtonClick'");
this.view_button_2 = view;
//view.setOnClickListener....
}
}
在ButterKnife生成的代码中,使用的仍然是R类。R2起到的作用仅仅是提供一个符号名,只要让程序知道在生成代码时对应哪一个变量即可。这个方法可以说是很“tricky”了。
Android库项目中的资源ID冲突的更多相关文章
- Android插件化(六): OpenAtlasの改写aapt以防止资源ID冲突
Android插件化(六): OpenAtlasの改写aapt以防止资源ID冲突 转 https://www.300168.com/yidong/show-2791.html 核心提示:引言And ...
- 如何在Android Studio项目中导入开源库?
前两天,谷歌发布了Android Studio 1.0的正式版,也有更多的人开始迁移到Android Studio进行开发.然而,网上很多的开源库,控件等还是以前的基于Eclipse进行开发,很多人不 ...
- 解决资源id冲突
--摘自<android插件化开发指南> 1.一套完整的Android App打包流程(Gradle方案) 第一步:aapt.为res目录下的资源生成R.java文件,同时为Android ...
- Android Studio 项目中集成百度地图SDK报Native method not found: com.baidu.platform.comjni.map.commonmemcache.JNICommonMemCache.Create:()I错误
Android Studio 项目中集成百度地图SDK报以下错误: java.lang.UnsatisfiedLinkError: Native method not found: com.baidu ...
- 【Android】项目中每个文件夹的作用
1. src:存放所有的*.java源程序. 2. gen:为ADT插件自动生成的代码文件保存路径,里面的R.java将保存所有的资源ID. 3. assets:可以存放项目一些较大的资源文件,例如: ...
- 解决项目中.a文件的冲突
.a文件是静态文件,有多个.o文件组合而成的,在ios项目开发中,当引用第三方库的时候,时不时的会碰到诸如库冲突.库包含了某些禁用的API等问题,而这些库往往都被打包成了静态库文件(即 .a文件)来使 ...
- 简述MVC框架模式以及在你(Android)项目中的应用
标题是阿里电话面试的问题,一直以为自己很清楚MVC模式,结果被问到时,居然没法将MVC和Android中各个组件对应起来,所以,面试肯定挂了,不过面试也是学习的一种方式,可以知道大公司看中什么,以及自 ...
- Android获取所有应用的资源id和对应的uri
背景 在某些应用中,为了实现应用apk资源放入重复利用,或者使用反射得到本应用的资源,需要使用反射方式获得,但Resources类中也自带了这种获取方式,并且功能更加强大 你可以获取string,co ...
- java web项目中打开资源文件中文乱码
1 java web项目中经常使用多模块管理.在某一个模块中添加了一些资源文件.但不是启动项目.有时候需要在程序中读取资源文件内容,打包后放到容器中就不能正常运行了.需要将所有资源文件放到启动项目的 ...
随机推荐
- C#调用WebService时插入cookie
SOAPUI插入Cookie的方法 SOAP插入cookie的方法如下,点击Head,点击加号,然后直接设置就可以了. C#中调用webService时插入Cookie 由于调用的时候必须要带上coo ...
- PyQt5多点触控写字板实现及困惑
Qt支持程序多点触控,就想使用PyQt5做一个触控画板,经过几番周折,查阅了TouchEvent官方文档,又参考了一篇QT for Android的例子,采用eventfilter过滤器来识别触屏事件 ...
- Android常见Crash类型分析(一)
问题1. java.lang.IllegalStateException: The specified child already has a parent. You must call remo ...
- VxWorks:添加自己组件到Tornado
项目要求将cpci的驱动做成Tornado组件,尝试了一下! Folder FOLDER_CPCI { //上层组件设置 NAME cpci componen ...
- [linux]device eth0 does not seem to be present, delaying initialization
mlite虚拟机启动出错,就把这个虚拟机删除掉重新建立,系统虚拟硬盘使用之前的,启动系统后不能上网,通过ifconfig查看网卡没启动,遂启动网卡服务,但是出错,就是:device eth0 does ...
- Maven入门(含实例教程)
原文地址:http://blog.csdn.net/u013142781/article/details/50316383 Maven这个个项目管理和构建自动化工具,越来越多的开发人员使用它来管理项目 ...
- INT_MAX (2147483647) 和INT_MIN (-2147483648)溢出
c语言中32位int型数据在运算的时候可能会出现溢出的情况,如: -2147483648-1会得到什么结果? -2147483648乘(-1)会得到什么结果? 2147483647+1会得到什么结果? ...
- css设置居中的方案总结
回想一下,自己平时项目里遇到的比较多的就是css如何让元素居中显示,其实差不多每种情况都遇到过,所采用的方法也都各有利弊,下面对这些方法来做个概括,对其中的坑点,也会一一指出来,希望能给遇到问题的同学 ...
- VS2010 EXCEL2010 表格操作的编程实现
参考: http://blog.csdn.net/wxfy1977/article/details/3847450(另外一种实现方式,数据库方式) http://blog.csdn.net/evkj2 ...
- springboot--springboot+mybatis多数据源最简解决方案
说起多数据源,一般都来解决那些问题呢,主从模式或者业务比较复杂需要连接不同的分库来支持业务.我们项目是后者的模式,网上找了很多,大都是根据jpa来做多数据源解决方案,要不就是老的spring多数据源解 ...