Android C++系列:访问Assets 文件夹.md

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: assets目录是Android的一种特殊目录,用于放置APP所需的固定文件,且该文件被打包到APK中时,不会被编码到二进制文件。 Android还存在一种放置在res下的raw目录,该目录与assets目录不同。

image.png


Java 层Assets


assets目录是Android的一种特殊目录,用于放置APP所需的固定文件,且该文件被打包到APK中时,不会被编码到二进制文件。 Android还存在一种放置在res下的raw目录,该目录与assets目录不同。 区别点:


  1. assets目录不会被映射到R中,因此,资源无法通过R.id方式获取,必须要通过AssetManager进行操作与获取;res/raw目录下的资源会被映射到R中,可以通过getResource()方法获取资源。
  2. 多级目录:assets下可以有多级目录,res/raw下不可以有多级目录。
  3. 编码(都不会被编码):assets目录下资源不会被二进制编码;res/raw应该也不会被编码。


Java层我们通过Context获取AssetsManager,然后调用AssetsManager的open方法获取assets下文件的输入流:


AssetManager am = getAssets();  
InputStream is = am.open("filename");  


JNI 层Assets


很多时候我们需要在JNI中直接操作Assets下的文件,比如我们要使用Assets下的图片资源作为Opengl贴图,或者使用Assets下的tflite模型文件加载模型。


加载纹理图片


在 OpenGL 开发中,我们要渲染一张图片,通常先是得到一张图片对应的 Bitmap ,然后将该 Bitmap 作为纹理上传到 OpenGL 中。在 Android 中有封装好的 GLUtils 类的 texImage2D 方法供我们调用。


public static void texImage2D(int target, int level, int internalformat,
             Bitmap bitmap, int type, int border)


该方法的底层原理实际上也是解析了该 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 目录下的图片:


InputStream is = getAssets().open(fileName); 


通过 AssertManager 的 open 方法直接拿到文件的输入流了。


而在 NDK 开发中同样的方式是行不通的,这里要采用另外一种方式,但其实意思都差不多的:


// NDK 中是 AssetManager
     AAssetManager *mgr = AAssetManager_fromJava(env, assetManager);
     // 打开 Asset 文件夹下的文件
     AAsset *pathAsset = AAssetManager_open(mgr, assetPath, AASSET_MODE_UNKNOWN);
     // 得到文件的长度
     off_t assetLength = AAsset_getLength(pathAsset);
     // 得到文件对应的 Buffer
     unsigned char *fileData = (unsigned char *) AAsset_getBuffer(pathAsset);
     // stb_image 的方法,从内存中加载图片
     unsigned char *contnet = stbi_load_from_memory(fileData, assetLength, &w, &h, &n, 0);


NDK 中可拿不到像 Java 那样的输入流,但是可以通过 AssetManager 的 AAsset_getBuffer 或者是 AAsset_read 方法去获取文件内容。


看到上面那两个 API 基本就稳了,再配合 stb_image 介绍过的方法,stbi_load_from_memory 从内存中加载图片的像素数据,最后就是 glTexImage2D 方法实现纹理上传。


Assets方法类封装


#include <string>
#include <vector>
#include <android/asset_manager.h>
#include "util_asset.h"
//#define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h>
bool
asset_read_file (AAssetManager *assetMgr, char *fname, std::vector<uint8_t>&buf) 
{
    AAsset* assetDescriptor = AAssetManager_open(assetMgr, fname, AASSET_MODE_BUFFER);
    if (assetDescriptor == NULL)
    {
        return false;
    }
    size_t fileLength = AAsset_getLength(assetDescriptor);
    buf.resize(fileLength);
    int64_t readSize = AAsset_read(assetDescriptor, buf.data(), buf.size());
    AAsset_close(assetDescriptor);
    return (readSize == buf.size());
}
uint8_t *
asset_read_image (AAssetManager *assetMgr, char *fname, int32_t *img_w, int32_t *img_h)
{
    int32_t  width, height, channel_count;
    uint8_t* img_buf;
    bool     ret;
    /* read asset file */
    std::vector<uint8_t> read_buf;
    ret = asset_read_file (assetMgr, fname, read_buf);
    if (ret != true)
        return nullptr;
    /* decode image data to RGBA8888 */
    img_buf = stbi_load_from_memory (read_buf.data(), read_buf.size(),
            &width, &height, &channel_count, 4);
    *img_w = width;
    *img_h = height;
    return img_buf;
}
void
asset_free_image (uint8_t *image_buf)
{
    stbi_image_free (image_buf);
}


总结


今天我们介绍了Android Assets文件夹使用,包括Java层和JNI层,并详细介绍了JNI层AAssetManager接口的使用。

目录
相关文章
|
7天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
25 4
|
1月前
|
Android开发
Android gradle task任务检查各个module之间资源文件冲突.md
Android gradle task任务检查各个module之间资源文件冲突.md
Android gradle task任务检查各个module之间资源文件冲突.md
|
6月前
|
Android开发
Android网络访问超时
Android网络访问超时
54 2
|
2月前
|
Java Android开发 C++
🚀Android NDK开发实战!Java与C++混合编程,打造极致性能体验!📊
在Android应用开发中,追求卓越性能是不变的主题。本文介绍如何利用Android NDK(Native Development Kit)结合Java与C++进行混合编程,提升应用性能。从环境搭建到JNI接口设计,再到实战示例,全面展示NDK的优势与应用技巧,助你打造高性能应用。通过具体案例,如计算斐波那契数列,详细讲解Java与C++的协作流程,帮助开发者掌握NDK开发精髓,实现高效计算与硬件交互。
129 1
|
2月前
|
JavaScript 前端开发 Java
通过Gtest访问C++静态、私有、保护变量和方法
通过Gtest访问C++静态、私有、保护变量和方法
67 0
|
3月前
|
XML 安全 Android开发
Flutter配置Android和IOS允许http访问
Flutter配置Android和IOS允许http访问
115 3
|
3月前
|
Android开发 iOS开发
[ionic]解决运行Android、IOS出现Could not find the web assets directory
[ionic]解决运行Android、IOS出现Could not find the web assets directory
37 1
|
4月前
|
Java Android开发 C++
🚀Android NDK开发实战!Java与C++混合编程,打造极致性能体验!📊
【7月更文挑战第28天】在 Android 开发中, NDK 让 Java 与 C++ 混合编程成为可能, 从而提升应用性能。**为何选 NDK?** C++ 在执行效率与内存管理上优于 Java, 特别适合高性能需求场景。**环境搭建** 需 Android Studio 和 NDK, 工具如 CMake。**JNI** 构建 Java-C++ 交互, 通过声明 `native` 方法并在 C++ 中实现。**实战** 示例: 使用 C++ 计算斐波那契数列以提高效率。**总结** 混合编程增强性能, 但增加复杂性, 使用前需谨慎评估。
140 4
|
3月前
|
JSON Android开发 C++
Android c++ core guideline checker 应用
Android c++ core guideline checker 应用
|
3月前
|
JSON Android开发 数据格式
Android c++ core guideline checker 应用问题之JSON compilation database的定义如何解决
Android c++ core guideline checker 应用问题之JSON compilation database的定义如何解决