Android C++ 系列:访问 Assets 文件夹
Java 层 Assets
assets 目录是 Android 的一种特殊目录,用于放置 APP 所需的固定文件,且该文件被打包到 APK 中时,不会被编码到二进制文件。Android 还存在一种放置在 res 下的 raw 目录,该目录与 assets 目录不同。区别点:
assets 目录不会被映射到 R 中,因此,资源无法通过 R.id 方式获取,必须要通过 AssetManager 进行操作与获取;res/raw 目录下的资源会被映射到 R 中,可以通过 getResource()方法获取资源。
多级目录:assets 下可以有多级目录,res/raw 下不可以有多级目录。
编码(都不会被编码):assets 目录下资源不会被二进制编码;res/raw 应该也不会被编码。
Java 层我们通过 Context 获取 AssetsManager,然后调用 AssetsManager 的 open 方法获取 assets 下文件的输入流:
JNI 层 Assets
很多时候我们需要在 JNI 中直接操作 Assets 下的文件,比如我们要使用 Assets 下的图片资源作为 Opengl 贴图,或者使用 Assets 下的 tflite 模型文件加载模型。
加载纹理图片
在 OpenGL 开发中,我们要渲染一张图片,通常先是得到一张图片对应的 Bitmap ,然后将该 Bitmap 作为纹理上传到 OpenGL 中。在 Android 中有封装好的 GLUtils 类的 texImage2D 方法供我们调用。
该方法的底层原理实际上也是解析了该 Bitmap ,得到了 Bitmap 所有的像素数据,类似于 Android NDK 关于 Bitmap 操作的 AndroidBitmap_lockPixels 方法,如果你不太了解该方法,可以参考《Android C++系列:JNI 操作 Bitmap》。
得到了所有像素数据之后,实际最终还是调用了 OpenGL 的 glTexImage2D 来实现纹理上传。当然,如果可以直接得到所有数据,也不需要走解析 Bitmap 这一步了,这种场景最常见的就是把相机作为输入了。
接下来我们会通过 Android NDK 开发中去渲染一张图片,步骤还是如上,从图像解析到纹理上传,不同的是我们将会解析 Assets 文件夹中的图片,而不是一张已经保存在手机 SDCard 上的图片。
相比于前者,SDCard 上的图片已经有了绝对地址了,直接把地址传到 stb_image 库就可以完成解析了(参考之前的文章 ),而 Assets 文件夹的内容在手机上可没有绝对地址。
一开始陷入了误区,想着怎么去获得文件的绝对地址,看到了 AssetManager 的 AAsset_openFileDescriptor 方法以为拿到文件描述符就万事大吉了,结果打印的地址是这样的 /proc/9941/fd/79
,这基本的不可用了。
换个思路,在 Java 中去加载 Assets 目录下的图片:
通过 AssertManager 的 open 方法直接拿到文件的输入流了。
而在 NDK 开发中同样的方式是行不通的,这里要采用另外一种方式,但其实意思都差不多的:
NDK 中可拿不到像 Java 那样的输入流,但是可以通过 AssetManager 的 AAsset_getBuffer 或者是 AAsset_read 方法去获取文件内容。
看到上面那两个 API 基本就稳了,再配合 stb_image 介绍过的方法,stbi_load_from_memory 从内存中加载图片的像素数据,最后就是 glTexImage2D 方法实现纹理上传。
Assets 方法类封装
总结
今天我们介绍了 Android Assets 文件夹使用,包括 Java 层和 JNI 层,并详细介绍了 JNI 层 AAssetManager 接口的使用。
版权声明: 本文为 InfoQ 作者【轻口味】的原创文章。
原文链接:【http://xie.infoq.cn/article/30fde1371a11da5620f64ae42】。文章转载请联系作者。
评论