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项目中经常使用多模块管理.在某一个模块中添加了一些资源文件.但不是启动项目.有时候需要在程序中读取资源文件内容,打包后放到容器中就不能正常运行了.需要将所有资源文件放到启动项目的 ...
随机推荐
- hadoop/storm以及hive/hbase/pig区别整理
STORM与HADOOP的比较 对于一堆时刻在增长的数据,如果要统计,可以采取什么方法呢? 等数据增长到一定程度的时候,跑一个统计程序进行统计.适用于实时性要求不高的场景.如将数据导到HDFS,再运行 ...
- http缓存(http caching)
通过使用缓存web网站和web应用的性能能够得到显著的提升.Web caches能够减小延迟和网络流量,从而缩短展示资源所花费的时间. 在http中控制缓存行为的首部字段是Cache-Control, ...
- 通用的Android控件抖动效果实现
这个小程序的功能在实际的开发中会用到,比如:设置Button左右晃动,或者上下的晃动效果,下面就给出示例代码. 首先:要定义一个xml文件,命名为Shake [html] view plain cop ...
- DriverStudio开发PCI设备DMA数据传输
DriverWizard向导可以创建基本的wDM驱动程序框架,包括总线类型,地址空间,中断源,DMA资源,以及IOCTL(i/o控制代码)的定义等等.详细情况可参看DriverStudio的帮助文档, ...
- Jlink 烧写Uboot
第一章 Hi3531_SDK_Vx.x.x.x版本升级操作说明 如果您是首次安装本SDK,请直接参看第2章. 第二章首次安装SDK 1.Hi3531 SDK包位置 在"Hi3531_V100 ...
- JavaScript获取屏幕和页面的宽度和高度
JavaScript获取屏幕和页面的宽度和高度 1.设计源码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN ...
- 不同WINDOWS平台下磁盘逻辑扇区的直接读写
不同WINDOWS平台下磁盘逻辑扇区的直接读写 关键字:VWIN32.中断.DeviceIoControl 一.概述 在DOS操作系统下,通过BIOS的INT13.DOS的INT25(绝对读).INT ...
- 【原】Java学习笔记029 - 映射
package cn.temptation; import java.util.HashMap; import java.util.Map; public class Sample01 { publi ...
- 芝麻HTTP:redis-py的安装
对于Redis来说,我们要使用redis-py库来与其交互,这里就来介绍一下它的安装方法. 1. 相关链接 GitHub:https://github.com/andymccurdy/redis-py ...
- h264多slice
1, h264编码以macroblock为最小单位(简称MB),多个连续的MB组成一个slice,每个slice编码输出一个NALU 2, 划分slice的方式可以按照固定个数MB的方式: 也可以以对 ...