【问题标题】:How does the mapping between android resources and resources ID work?android资源和资源ID之间的映射是如何工作的?
【发布时间】:2011-06-29 07:30:56
【问题描述】:

Android 只需通过 R.id.XXX 就可以找到合适的资源是很神奇的。

AFAIK,资源被编译成二进制格式,那么这个映射逻辑是如何工作的呢?

也许它是这样工作的:

例如,在 layout1.xml 中,我们得到:

<Button android:id="@+id/button1" >

AAPT 会在 R.java 中生成这个:

public static final int button1=0x7f05000b;

生成*.apk时,@+id/button1会被替换为“0x7f05000b”。

因此,当我们调用时:

findViewById(R.id.button1);

我们基本上仍然是根据 ID 进行搜索,尽管 ID 是一个像 0x7f05000b 这样的数字。

谢谢!

添加

我真正想知道的是,资源id整数是如何解析成资源内容的?也就是说,Android运行时是如何定位以资源id为唯一线索的资源内容的?

例如,如何找到带有资源 id 的可绘制图片?或者如何找到带有资源 id 的字符串值?

【问题讨论】:

    标签: android android-resources


    【解决方案1】:

    在构建时,aapt 工具会收集您定义的所有资源(尽管是单独的文件或文件中的显式定义)并为它们分配资源 ID。

    资源 ID 是一个 32 位数字,格式为:PPTTNNNN。 PP 是资源用于的包; TT 是资源的类型; NNNN 是该类型中资源的名称。对于应用程序资源,PP 始终为 0x7f。

    TT 和 NNNN 值由 aapt 任意分配——基本上对于每种新类型,都会分配和使用下一个可用数字(从 1 开始);同样,对于类型中的每个新名称,都会分配和使用下一个可用编号(从 1 开始)。

    因此,如果我们让 aapt 按此顺序处理这些资源文件:

    layout/main.xml
    drawable/icon.xml
    layout/listitem.xml
    

    我们看到的第一个类型是“layout”,因此被赋予 TT == 1。该类型下的第一个名称是“main”,因此被赋予 NNNN == 1。最终资源 ID 为 0x7f010001。

    接下来我们看到“drawable”,因此它被赋予 TT == 2。该类型的名字是“icon”,因此得到 NNNN == 1。最终资源 ID 是 0x7f020001。

    最后我们看到另一个“布局”,它和以前一样具有 TT == 1。这有一个新名称“listitem”,以便获得下一个值 NNNN == 2。最终资源 ID 为 0x7f010002。

    请注意,默认情况下 aapt 不会尝试在构建之间保持这些标识符相同。每次资源发生变化时,它们都可以获得新的标识符。每次构建它们时,都会使用当前标识符创建一个新的 R.java,以便您的代码获得正确的值。因此,您绝不能将资源标识符保留在可用于不同应用构建的任何地方。

    编译资源并分配标识符后,aapt 会为您的源代码生成 R.java 文件和名为“resources.arsc”的二进制文件,其中包含所有资源名称、标识符和值(用于来自单独的文件,它们的值是 .apk 中该文件的路径),其格式可以在运行时在设备上轻松映射和解析。

    您可以使用命令“aapt dump resources ”获取 apk 中 resources.arsc 文件的摘要。

    二进制资源表的格式记录在资源数据结构的头文件中:

    https://github.com/android/platform_frameworks_base/blob/master/libs/androidfw/include/androidfw/ResourceTypes.h

    设备上读取资源表的完整实现在这里:

    https://github.com/android/platform_frameworks_base/blob/master/libs/androidfw/ResourceTypes.cpp

    【讨论】:

    • 顺便说一句,这些细节是否记录在某处?
    • 除了我在这里链接的结构定义之外,没有记录实现细节。
    • 顺便说一句,恕我直言,“RTFSC”是错误的。来源是对规范的解释,可以更改。如果没有规范,则该行为是正式未定义的。阅读源代码只会告诉您作者在编写规范时认为规范的含义。它告诉你现在会发生什么,而不是以后会发生什么。尽管如此,Dianne 的出色解释可能会为您提供您需要了解的内容,并在可预见的未来保持相关性。
    • @edward-falk:我认为 RTFSC 在这里是正确的,因为首先的问题是关于引擎盖下的东西(从未正式指定),而不是 API。内部规范是 SC :)
    • 非常感谢@hackbod!我很高兴我可以在 LeakCanary 中使用它来将 id 资源 id 的映射推到它们对应的名称上。 github.com/square/leakcanary/pull/1663
    【解决方案2】:

    如果您对内部实现(设备端)感兴趣,请查看Resources.java 中的 loadDrawable()。有关extracting data from the resource table 的信息,请参阅hackbod 的优秀答案

    要了解如何将布局从资源 ID 转换为视图,请查看 LayoutInfater.java

    【讨论】:

      【解决方案3】:

      据我了解,aapt 将为您的每个资源自动生成唯一 ID,并将它们存储在查找表中。该查找表作为位于“bin/resources.ap_”中的“resources.arsc”文件保存(这只是一个 ZIP 文件,因此请随意使用您喜欢的 ZIP 查看器打开)。查找表也作为 R.java 持久化,如您所知,它允许您在 Java 中引用您的资源。

      如果您想了解有关 ARSC 文件的更多信息,我建议您使用 Google 搜索,或查看 http://code.google.com/p/android-apktool/ 的代码。

      -丹

      【讨论】:

      • 感谢您提供重要线索。我会搜索一下。如果没有更好的答案出现,我会标记你的答案。谢谢。
      【解决方案4】:

      最后一点:很长一段时间以来,我都没有使用相对布局,因为许多项目需要在 xml 文件中进一步引用项目,而我不知道如何引用 @id/foo 尚未定义。

      <!-- doesn't work -->
      <TextView android:layout_above="@id/foo">above</textview>
      <TextView android:id="@+id/foo">below</textview>
      

      然后有一天我意识到(duh)你可以在引用中定义一个 id;它不必在带有 id 的元素中:

      <!-- works -->
      <TextView android:layout_above="@+id/foo">above</textview>
      <TextView android:id="@id/foo">below</textview>
      

      【讨论】:

        【解决方案5】:

        神奇之处在于 Eclipse 插件和它在应用程序的“gen”文件夹中自动生成的 R.java 文件。如果您查看此文件,您将看到 R.xx.XXX 中每个 XXX 的静态映射,其中 xx 可以是动画、数组、颜色和所有其他资源类型。

        【讨论】:

          猜你喜欢
          • 2018-03-28
          • 2014-02-11
          • 1970-01-01
          • 2021-11-19
          • 2016-05-06
          • 1970-01-01
          • 1970-01-01
          • 2015-02-26
          相关资源
          最近更新 更多