/res和/assets目录之间的区别

291

我知道res目录中的文件可以从R.class访问,而assets则像文件系统一样运行,但是我想知道,在一般情况下,何时使用其中之一更好。
有人能帮我了解resassets之间的真正区别吗?

9个回答

307

在资源中,有内置的支持提供不同语言、操作系统版本、屏幕方向等的备选方案,如此处所述。这些都不能用于资产。此外,API 的许多部分支持使用资源标识符。最后,资源的名称会转换为常量字段名称,在编译时进行检查,因此代码与资源本身之间的不匹配机会更小。这些都不适用于资产。

那么为什么需要资产文件夹呢?如果你想在运行时计算要使用的资产,这很容易。对于资源,您需要声明可能使用的所有资源 ID 列表并计算列表中的索引。(这有点棘手,并且在开发周期中资源集发生变化时会出现错误的机会。)(编辑:您可以使用getIdentifier按名称检索资源 ID,但这会失去编译时检查的好处。)资产还可以组织成文件夹层次结构,而此功能不受资源支持。这是一种不同的数据管理方式。虽然资源覆盖了大多数情况,但资产偶尔也有用处。

另一个不同之处:在库项目中定义的资源会自动导入到依赖于该库的应用程序项目中。对于资产,这种情况并不会发生;资产文件必须存在于应用程序项目的资产目录中。(编辑:在 Android 的新基于 Gradle 的构建系统(与 Android Studio 一起使用)中,这不再是真实的。库项目的资产目录被打包到 .aar 文件中,因此在引用库时,库项目中定义的资产将合并到应用程序项目中(因此如果它们在引用的库中,则不必存在于应用程序的 /assets 目录中)。)

编辑:如果你想将自定义字体与你的应用程序一起打包,另一个区别就出现了。可以使用API调用从文件系统或应用程序的assets/目录中创建Typeface。但是,没有API可以从res/目录中存储的字体文件(或InputStream)创建Typeface(这会允许使用res/目录)。[注意:在Android O中(现在可用于alpha预览版),您将能够将自定义字体作为资源包含。请参见此迫切需要的功能的描述here。但是,只要您的最低API级别为25或更低,您就必须将自定义字体作为资产而不是作为资源进行打包。]


2
res 代表资源,那么 assets 代表什么? - Vivek Warde
45
@vwvwvwvwvwvwvwvwvw - 嗯……它并不代表任何东西;它只是意味着“资产”(即英语单词“asset”的复数形式:一个有用或有价值的东西)。 - Ted Hopp
1
能否写入 raw/ 目录中的文件? - Prince
7
@Prince - 不可以更改/res/assets目录中的任何内容,因为它们被打包在.apk文件中并且是只读的。 - Ted Hopp
@MaksimKniazev - 对于这种情况,我猜从资源中加载图像比从资产中加载图像效率更高。RecyclerView 喜欢使用 Drawable 对象进行操作,而通过从资产中加载图像无法获得这些对象;您需要在代码中使用 BitmapDrawable 包装生成的位图。总的来说,我认为(虽然我可能对此有所误解)从 res 中加载和从资产中加载使用相同的基础加载机制,因此任何性能差异最多只是次要的。 - Ted Hopp
显示剩余3条评论

72

两者非常相似。真正的主要区别在于,在res目录中,每个文件都被赋予一个预编译的ID,可以通过R.id.[res id]轻松访问。这对于快速轻松地访问图片、声音、图标等非常有用。

assets目录更像一个文件系统,并提供更多自由将任何文件放入其中。然后,您可以像访问Java中的任何文件系统中的任何文件那样访问该系统中的每个文件。该目录适用于游戏详情、字典等内容。


5
如果我们想在应用程序中添加外部字体,则将它们放置在资产文件夹中。 - Tushar Pandey
2
@TusharPandey 现在我们不必将字体放在“Assets”文件夹中,Android系统现在包含“fonts”目录,我们可以将自定义字体文件放在那里,或者我们可以让Android Studio为我们下载它。 - CopsOnRoad
@Jack,这是针对奥利奥的。 - Tushar Pandey
1
@TusharPandey 它被添加到奥利奥(Oreo)中,但是它已经被向后移植到 Android 支持库,早在 API 级别 16。 - CopsOnRoad

42

我知道这已经很旧了,但为了澄清,官方android文档中有每个文件夹的解释:

来自http://developer.android.com/tools/projects/index.html

assets/

该文件夹为空。您可以使用它来存储原始资产文件。在此保存的文件将按原样编译为.apk文件,并保留原始文件名。您可以使用URI以及使用AssetManager将文件作为字节流读取的方式,像典型的文件系统一样导航到此目录。例如,这是纹理和游戏数据的好位置。

res/raw/

用于任意原始资产文件。将资产文件保存在此处而不是assets/目录中只是在访问它们的方式上有所不同。这些文件被aapt处理,必须使用R类中的资源标识符从应用程序引用它们。例如,这是媒体文件(如MP3或Ogg文件)的良好位置。


5
我认为这会使问题更加混淆——纹理和ogg文件都是大块数据,你将其馈送到用户空间API(openGL/MediaPlayer)中...他们为什么要在这里区分呢? - user755921

14
以下是一些关键要点:
  1. 原始文件必须具有有效的Java标识符名称,而资产文件没有位置和名称限制。换句话说,它们可以被分组在任何目录中。
  2. 从Java和xml中很容易引用原始文件(即您可以从清单或其他xml文件中引用原始文件)。
  3. 将资产文件保存在此处而不是在资产/目录中仅在访问它们的方式上有所不同,如此处所述http://developer.android.com/tools/projects/index.html
  4. 在库项目中定义的资源会自动导入到依赖于该库的应用程序项目中。对于资产文件,情况并非如此;资产文件必须存在于应用程序项目的资产目录中。
  5. 资产目录更像文件系统,提供了更多的自由度,可以在其中放置任何文件。然后,您可以像通过Java访问任何文件系统中的任何文件一样访问该系统中的每个文件,例如游戏数据文件、字体、纹理等。
  6. 与资源不同,资产可以在资产目录中组织为子文件夹。但是,您可以使用资产获取输入流时的唯一操作。因此,在资产中存储字符串或位图没有太多意义,但是您可以存储自定义格式的数据,例如输入校正字典或游戏地图。
  7. 原始文件可以通过生成R.java文件来进行编译时检查,但是如果要将数据库复制到私有目录,则可以使用资产进行流式传输。

结论

  1. Android API包括一个非常方便的资源框架,也针对各种移动应用程序的大多数典型用例进行了优化。您应该掌握资源并尝试在可能的情况下使用它们。
  2. 但是,如果您需要针对特殊情况的更高灵活性,则可使用资产提供更低级别的API,以允许更高程度的资源组织和处理。

1
根据被接受的答案,对于使用Gradle构建系统的项目来说,第4点不再适用;库中的资源将被捆绑到应用程序项目的资产集合中。 - Venryx

4
如果您需要在Java代码中引用它们,最好将文件放入“res”目录中。而所有位于res文件夹中的文件都将被索引到R文件中,这使得加载它们变得更快(也更容易!)。

7
你可以从Java中访问assets文件。 - Heiko Rupp
2
是的,但它们没有被索引-->这将会变得更慢,这是移动开发中的一个真正的问题。 - L.Butz
那么,res/raw和asset有什么不同呢?res/raw也会被索引吗? - anticafe
检查@TedHopp的帖子-他非常清晰地给出了答案。 - L.Butz

1
使用类似文件系统的资产来转储任何类型的文件。并使用res存储其用途,布局、图像、值等。

0
虽然不常见,但您可能需要访问原始文件和目录。如果确实需要这样做,那么将文件保存在res/目录下将无法满足您的需求,因为从res/目录中读取资源的唯一方式是使用资源ID。相反,您可以将资源保存在assets/目录中。

0
Ted Hopp已经很好地回答了这个问题。我一直在使用res/raw来存储我的OpenGL纹理和着色器文件。我曾考虑将它们移动到assets目录以提供分层组织。
但是,这个线程让我放弃了这个想法。首先,因为我喜欢使用唯一的资源ID。其次,使用InputStream/openRawResource或BitmapFactory读取文件非常简单。第三,能够在可移植库中使用非常有用。

1
InputStreams和BitmapFactory支持的第二点对于资源和资产都是正确的(请参见此处:https://dev59.com/kWoy5IYBdhLWcg3wguWS#8501428)。第三点不再适用。(请参见已接受的答案) - Venryx
第一点也是有问题的,因为在资产中,我们不能提供相同的文件名。res/raw的好处更多地是确保文件名始终正确。 - Elye

-6

资源提供了一种在应用程序中包含任意文件(如文本、XML、字体、音乐和视频)的方式。如果您尝试将这些文件作为“资源”包含,Android 将会将它们处理到其资源系统中,您将无法获取原始数据。如果您想访问未经处理的数据,资源是其中一种方法。


6
这是不正确的。如果将数据放在res/raw目录下,你可以使用openRawResource(resourceName)方法来获取原始数据。 - Ted Hopp
我在一个库中有opengles纹理和着色器文件。我必须使用res/raw来存储它们。我使用InputStream和BitmapFactory来读取它们。我将把这个作为独立的注释添加。 - dturvene
2
看起来是从 Xamarin Microsoft 文档中复制的。在复制文本时请注明出处。https://learn.microsoft.com/en-us/xamarin/android/app-fundamentals/resources-in-android/android-assets?tabs=vswin - Sjoerd Pottuit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接