热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android库项目中的资源ID冲突的解决方法

本篇文章主要介绍了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 中通过代码模拟用户的点击和滑动操作,包括参数说明、事件生成及处理逻辑。详细解析了视图(View)对象、坐标偏移量以及不同类型的滑动方式。 ... [详细]
  • 深入理解OAuth认证机制
    本文介绍了OAuth认证协议的核心概念及其工作原理。OAuth是一种开放标准,旨在为第三方应用提供安全的用户资源访问授权,同时确保用户的账户信息(如用户名和密码)不会暴露给第三方。 ... [详细]
  • 本文总结了2018年的关键成就,包括职业变动、购车、考取驾照等重要事件,并分享了读书、工作、家庭和朋友方面的感悟。同时,展望2019年,制定了健康、软实力提升和技术学习的具体目标。 ... [详细]
  • 在计算机技术的学习道路上,51CTO学院以其专业性和专注度给我留下了深刻印象。从2012年接触计算机到2014年开始系统学习网络技术和安全领域,51CTO学院始终是我信赖的学习平台。 ... [详细]
  • CSS 布局:液态三栏混合宽度布局
    本文介绍了如何使用 CSS 实现液态的三栏布局,其中各栏具有不同的宽度设置。通过调整容器和内容区域的属性,可以实现灵活且响应式的网页设计。 ... [详细]
  • Linux 系统启动故障排除指南:MBR 和 GRUB 问题
    本文详细介绍了 Linux 系统启动过程中常见的 MBR 扇区和 GRUB 引导程序故障及其解决方案,涵盖从备份、模拟故障到恢复的具体步骤。 ... [详细]
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • 本文将详细介绍如何使用剪映应用中的镜像功能,帮助用户轻松实现视频的镜像效果。通过简单的步骤,您可以快速掌握这一实用技巧。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文介绍如何在 Xcode 中使用快捷键和菜单命令对多行代码进行缩进,包括右缩进和左缩进的具体操作方法。 ... [详细]
  • 本文介绍了一款用于自动化部署 Linux 服务的 Bash 脚本。该脚本不仅涵盖了基本的文件复制和目录创建,还处理了系统服务的配置和启动,确保在多种 Linux 发行版上都能顺利运行。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • Android 渐变圆环加载控件实现
    本文介绍了如何在 Android 中创建一个自定义的渐变圆环加载控件,该控件已在多个知名应用中使用。我们将详细探讨其工作原理和实现方法。 ... [详细]
  • 如何在WPS Office for Mac中调整Word文档的文字排列方向
    本文将详细介绍如何使用最新版WPS Office for Mac调整Word文档中的文字排列方向。通过这些步骤,用户可以轻松更改文本的水平或垂直排列方式,以满足不同的排版需求。 ... [详细]
  • 本文总结了在使用Ionic 5进行Android平台APK打包时遇到的问题,特别是针对QRScanner插件的改造。通过详细分析和提供具体的解决方法,帮助开发者顺利打包并优化应用性能。 ... [详细]
author-avatar
mobiledu2502910337
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有