Android中的资源和R.java类

在Android项目中的res目录中包含了项目使用的各种资源,这些资源全部都分布在res目录下的各个子目录中。每个资源都有两个属性,一个是资源的名字,一个是资源的类型。此外,res目录下的资源在编译后都会有一个对应的id。

R.java类(以下简称R类)是Android中一个非常重要的类,其中定义了res目录中全部资源的id。在代码中通过R类获取到资源的id后,即可调用Android API来获取和使用对应的资源。例如:

ImageView imageView = (ImageView)findViewById(R.id.imageView);
TextView textView = (TextView)findViewById(R.id.textView);
imageView.setImageResource(R.drawable.bg)
textView.setText(R.string.app_name)

R类的生成

R类并不包含在项目代码中,而是由Android SDK在编译阶段通过aapt工具生成的。一般情况下,开发者不需要关注R类的生成,直接在代码中使用即可。然而在某些情况下需要由开发者手动生成R类,并放到项目代码中。

为什么需要手动生成R.java类

Android library

Android 项目根据用途的不同分为app项目和library项目。app项目用来生成可以在Android系统上运行的apk程序,提交到应用市场给用户使用。而library项目并不会生成apk,而是生成一个sdk,提供给其他开发者使用。

对library项目来说,如果library工程中包含了资源,如layout,drawable,string,dimen等,那么需要将这些资源文件和编译后的代码一起放到sdk中。对Eclipse library工程,是将整个res目录原样放到sdk中。对Android Studio的library工程,会将整个res目录打包到aar文件中。

Android library工程中的R类

对包含资源的library工程来说,和app工程一样,需要在代码中通过资源id来获取对应的资源。同样可以在library工程代码中通过library工程包名下的R类来获取对应资源的id。但是library工程中的R类并不会包含在library工程编译后的jar包或arr包中,也就是说,library工程开发完成后,提供给其他开发者的sdk中并没有包含这个R类。这个R类同样是在app工程编译时生成的。那么app工程在编译时是如何知道需要生成library工程包名下的R类呢?

一个app工程可以包含多个library工程(在Android Studio中称为module)。当一个app工程在编译时,会遍历其所引用的所有library工程,生成各个library工程对应的R.java类。这里会用到每个library sdk中提供的另外一个文件AndroidManifest.xml。在library工程的AndroidManifest.xml文件中包含了各个library工程的包名称,通过这个文件就可以知道各个library工程的包名,从而生成各个library工程包名下的R类。

例如,一个被引用的library工程中的AndroidManifest.xml文件中配置的package=”com.cclink.mylib”,则app工程在编译时会为该library工程生成一个com.cclink.mylib.R的类。

最后app工程中全部java代码,app工程的R类,所有library工程的R.java类,都会被编译,并和所有library工程的jar包合并到一起,然后再转换成dex文件。

Android library sdk的特殊使用方式

一个library工程开发完成后会以sdk的形式提供给其他开发者来使用。如前所述,其他开发者在开发app时,如果通过引用的方式来使用该library sdk,则app工程编译时会自动为该library sdk下的资源生成一个对应的R类,并打包到apk中。
然后,app的开发者并不总是希望通过在Android Studio中添加工程引用的方式来使用一个library sdk。他们有时需要将library sdk中的jar和res直接拷贝到app工程来使用,有时需要将所有依赖的sdk中的jar和res拷贝到一起,合并成一个library来使用。
这样做有时是为了方便,减少工作量,有时则是没有办法通过工程引用方式来使用library。这在游戏开发时非常常见。很多游戏引擎只提供了一个目录,用来放置各类第三方的Android插件,根本就找不到一个Android的主工程,来建立引用关系。APK的编译打包也是在游戏引擎中封装好了,并不需要通过Eclipse或Android Studio来打包。
例如,Unity3D中通常都是将Android插件(也就是Android library工程对应的sdk)直接拷贝到/Assets/Plugins/Android目录中来使用,虽然Unity3D提供了导出Android工程的功能,但使用起来非常麻烦。开发者很可能不希望这样去做。

Android library sdk特殊使用方式带来的问题

如果将sdk中的文件拷贝到app工程中来使用,或者合并所有的sdk到一个library中,由于这时所有library都混合在一起,app在编译时根本就找不到原先各个library中的AndroidManifest.xml文件,也就不会为各个library生成对应的R.java类。当app在运行时,library中代码由于找不到对应的R类,会出现ClassNotFound的异常。Unity虽然支持各个sdk分开存放,不需要将sdk合并到一起,但是Unity编译生成的apk文件中不会包含任何R类,即使是app包名下的R类也不会生成。

解决没有R类带来的问题

要解决没有R类的问题,需要从两个角度来考虑。

从library开发者的角度来看,需要让library工程编译后的sdk在这种没有R类情况下能够正常使用。为了达到此目的,需要将代码中所有通过包名下R类来获取资源id的方式替换为其他的实现方式。Android SDK提供了getResources().getIdentifier()方法来获取资源id,library开发者可以手动将代码中所有需要使用资源id的地方用这种方式来获取。但getResources().getIdentifier()的参数是资源的名字,是一个字符串,这样在获取资源id时,就不能使用IDE的代码提示,自动补全等功能了。

从app开发者的角度来看,如果需要使用的sdk没有对这种使用方式做兼容,而且不能通过引用方式来使用该sdk时,需要能够让该sdk打包到apk后能够正常运行。为了达到此目录,需要为该sdk生成一个该sdk包名下的R类,然后将该R类编译打包到apk中。这样sdk中的代码在运行就不会找不到对应的R类了。

AndroidRClassGenerator工具

AndroidRClassGenerator是一个用来生成R.java类的Python脚本,基于Python2.7版本。它可以以两种方式生成R.java类。工具下载地址附在文章最后。
对library开发者来说,AndroidRClassGenerator能够生成一个新的R类,新的R类中使用了不同的方式来获取资源id,同时提供了和原先R类一样的资源访问的方式。因此,library开发者可以像开发app一样使用R类来获取资源,只需要将代码中原先的import R类的信息替换替换为新的R类即可访问到对应的资源id。AndroidRClassGenerator可同时生成R类,并完成代码中import信息的替换。
对app开发者来说,AndroidRClassGenerator可以根据library工程中的res目录,和指定的包名来生成对应的R类。

参数配置

在使用前需要先配置config.ini,config.ini中各个参数的含义如下。

  1. ProjectOrResDir:表示library工程的路径(可以是Eclipse的工程,也可以是Android Studio的工程)。也可以直接指定到某个资源目录。
  2. sdkdir:Android SDK的路径
  3. RClassPackage:要生成的目标R类的包名
  4. ReplaceCode:是否替换代码中的import信息,true表示替换,false表示不替换。
    例如library工程的包名为x.y.z,RClassPackage为a.b.c,则当ReplaceCode为true时除了会生成一个a.b.c.R.java类外,还会自动将代码中所有的import x.y.z.R,改为import a.b.c。然代码通过新生成的R类来获取资源id。
    只有ProjectOrResDir配置的library工程的路径时此参数才有效,当ProjectOrResDir配置的是资源目录时,此项配置会被忽略。

library开发者的使用方法

对library开发者来说,需要生成一个包含指定包名的R类放到代码中,然后将其他代码中所有用到默认R类(即该library工程包名下R类)的地方都替换为生成的R类。
具体配置流程如下。

  1. 配置ProjectOrResDir为工程的路径,可以是Eclipse的工程,也可以是Android Studio的工程。
  2. 配置sdkdir为本地的Android SDK的路径
  3. 配置RClassPackage为需要生成的R类的包名,此包名不可和library的包名完全相同。以免在工程编译时出现相同类名的编译错误。
  4. ReplaceCode一般应配置为true。如果已经替换过一次,且没有新的代码需要替换,可以设置为false。
  5. 运行RClassGenerator.py
  6. 在library工程入口代码处(例如初始化,或者第一个界面显示时)调用新生成R类的R.init(context)方法,context是某个Context对象。

app开发者的使用方法

对app开发者来说,需要生成一个包含library包名的R类放到app工程的代码中。
具体配置流程如下。

  1. 如果是Eclipse或完整的Android Studio的library工程,配置ProjectOrResDir为library工程中res目录的路径,如果是单独的aar文件,需要先将res目录从aar文件中解压出来,然后配置ProjectOrResDir为解压后的res目录路径。
  2. 配置sdkdir为本地的Android SDK的路径。
  3. 配置RClassPackage为library的包名,library的包名可以在library工程,或aar压缩包中的AndroidManifest.xml文件中找到。
  4. ReplaceCode参数在ProjectOrResDir配置为资源目录时会被自动忽略。这里不需要配置。
  5. 运行RClassGenerator.py。
  6. 生成的R.java类在res目录的同级目录中,将生成的R类拷贝到app项目中,这可能是一个app工程,也可能是工程中某个可以编辑java代码的目标,有时甚至需要手动将R类编译成class并打包成jar放到app工程中。
  7. 在app工程某个入口处调用新生成R类的R.init(context)方法,context是某个Context对象。这可能是在主Activity的onCreate()中调用,也可能是通过jni方式调用。

实现原理

通过用AndroidRClassGenerator生成R类代替了原本应该在app编译时生成的R类,用这种方式来让library中代码能够正确的得到资源id。由于资源id只有在apk编译时才能最终确定,所以AndroidRClassGenerator生成的R类不能用某次编译时的常量来代替。
有两种方式可以获取到资源id:一种是通过反射app包名下的R类来获取,一种是通过context.getResources().getIdentifier()方法来获取。
通过反射方式来获取资源id的原理是,虽然app编译时不会为每个library生成单独的R类,但是仍然会生成一个app包名下的R类,这个R类中包含了所有library中的资源id(当然也还包含app自身的全部资源的id),通过反射读取这个R类,可以得到对应的资源id。
context.getResources().getIdentifier()是android sdk提供的一个api,通过此接口可以获取到指定类型,指定名称的资源id。
AndroidRClassGenerator生成的R类同时包含了这两种实现方式,它首先会尝试通过反射方式来获取系统资源id,如果反射方式获取失败,再尝试用context.getResources().getIdentifier()方式来获取。
之所以先用反射方式来获取资源id,是因为反射方式比context.getResources().getIdentifier()方式要快得多。但反射方式的缺点是必须要存在一个app包名下的R类。如果这个类不存在(例如,通过Unity3D直接编译出的apk中就不包含R类),则反射方式失效。
无论有没有app包名下的R类,context.getResources().getIdentifier()都能够获取到资源id,但context.getResources().getIdentifier()方式比反射方式要慢一些。

工具路径:http://download.csdn.net/download/ccpat/9443653

Android Studio的打包问题

Android Studio从某个版本(具体哪个版本没有去考究)开始,在编译时会自动删除项目中的R.class类,这样在运行时会出现找不到R类的问题。这时只需要在脚本中将R.java类改为一个其他的名字就可以了。也就是将”public final class R”改为”public final class XXXR”,同时将destRClassFile = os.path.join(filePath, ‘R.java’)改为destRClassFile = os.path.join(filePath, ‘XXXR.java’)。

Android R.java类的手动生成相关推荐

  1. Android工程中R.java文件的重新生成——注意资源文件的错误

    一.如果二或三步骤还是不能生成R.java中内容,一般是XML文件有错,或者xml引用的资源文件找不到,用排除法删除xml后执行二或者三的步骤. 二. 在Android中使用开源代码或者自己开发项目的 ...

  2. 导入android源码有错,R.java文件不能自动生成解决方法 http://caizi12.iteye.com/blog/975125

    最近几天学习android ,学习时候难免要导入一些示例,目的为了更加了解android各种API用法,顺便也可以学习下别人代码的写法.可是导入android源码后,基本都有错误,R.java也不会自 ...

  3. jaxb 生成java类_重用生成的JAXB类

    jaxb 生成java类 在本文中,我将演示如何利用XJC扩展来重用以前从XML模式生成的类. 当其他XML架构导入XML架构并且您不想每次都生成相同的类时,这很有用. 导入的架构(Product.x ...

  4. android 调用java类_Android中在WebView里实现Javascript调用Java类的方法

    搜索热词 为了方便网页和Android应用的交互,Android系统提供了WebView中JavaScript网页脚本调用Java类方法的机制.只要调用addJavascriptInterface方法 ...

  5. android r.java 原理,深入理解Android消息处理系统原理

    Android应用程序也是消息驱动的,按道理来说也应该提供消息循环机制.实际上谷歌参考了Windows的消息循环机制,也在Android系统中实现了消息循环机制. Android通过Looper.Ha ...

  6. proto生成java类_.proto生成java或其他语言

    > .proto生成java或其他语言 1. 首先看看一个简单的service.proto文档 ~~~ syntax = "proto3"; option java_pack ...

  7. android r类 作用,Android 主项目和 Module 中 R 类的区别

    Android 主项目和 Module 中 R 类的区别 我们知道 Android 项目中会通过自动生成一个 R.java 类的方式来保存项目中所有资源文件的标识在主项目中生成的 R.java 中的资 ...

  8. Android下HelloWorld项目的R.java文件介绍

    R.java文件介绍 HelloWorld工程中的R.java文件 package com.android.hellworld; public final class R {     public s ...

  9. Android Apk瘦身方案1——R.java文件常量内联

    R.java 文件结构 R.java 是自动生成的,它包含了应用内所有资源的名称到数值的映射关系.先创建一个最简单的工程,看看 R.java 文件的内容: R文件生成的目录为app/build/gen ...

最新文章

  1. 模板 - 输入输出优化
  2. 谁在引领中国制造?中国智能制造发展解析
  3. 手术革命:这三家公司如何用AR技术辅助医疗手术
  4. 天猫上线“商家售后服务评价”功能,消费者体验将纳入商家考核指标
  5. 三星有网络显示无网络连接到服务器,三星s5296连接了网络为什么不能用 看完这些原因就知道了...
  6. Linux select 一网打尽
  7. java编写母亲节快乐_写在母亲节来临之际
  8. 框架 butterknife
  9. cvs 代码无法提交
  10. System V 共享内存 和 系列函数
  11. 【虚拟机】VMware启动时报错:该虚拟机似乎正在使用中....请按“获取所有权(T)”按钮获取它的所有权
  12. php if多条件_通过PHP与Python代码对比浅析语法差异
  13. 制造业各细分行业数据库(2000-2019年)
  14. 几款比较有名的刷流量软件
  15. chrome插件之网页翻译插件
  16. 每日新闻丨软件业务收入64616亿元;工信部发话广电5G正式启动
  17. CVPR2017 | G-RMI_Google大佬构建的姿态估计baseline
  18. java runnable执行完_java – 如何停止Runnable计划在一定数量的执行后重复执行
  19. java生成word报告echart_Java这几个用 Pyecharts 做出来的交互图表,领导说叼爆了!...
  20. FIFO最小深度计算

热门文章

  1. 计蒜客题解——T1414:抠图
  2. java-集合-set(不重复集合)知识分解——庖丁解牛版
  3. FPGA和CPLD芯片选型介绍(三)
  4. 微信小程序零基础入门(上)
  5. 【进阶4-3期】面试题之如何实现一个深拷贝
  6. SSR 它到底香不香?细数 SSR 的利与弊
  7. Web前端大作业——城旅游景点介绍(html css javascript) html旅游网站设计与实现
  8. 01_ACS550变频器RS485Modbus通信-通讯连接
  9. 小米校招编程题:数组乘积、异形数、朋友圈
  10. 【office各个版本安装包】