Android NDK:从共享库中读取assets内的文件

12
在我的应用程序中,我需要将位于“assets”文件夹中的文件传递到“shared library”,但现在无法使用jni来实现此操作。在我的项目中,我正在使用预编译的共享库,在其中我已经将我的文件路径硬编码,但是我收到了“找不到文件或目录”的错误。因此,在我的.apk文件中,我有一个.so文件位于“libs/armeabi-v7a”文件夹中,并且我的文件位于“/assets”文件夹中。
我尝试过像这样做:
char *cert_file = "/assets/cacert.cert";
av_strdup(cert_file);

还有其他一些路径,但都无法正常工作。

这真的可能吗?

3个回答

21

您可以在C++中简单地使用AAssetManager类。

基本上,您需要:

  • 在库的初始化期间获取指向AAssetManager* assetManager的指针
  • 使用它来读取您的文件:

    // Open your file
    AAsset* file = AAssetManager_open(assetManager, filePath, AASSET_MODE_BUFFER);
    // Get the file length
    size_t fileLength = AAsset_getLength(file);
    
    // Allocate memory to read your file
    char* fileContent = new char[fileLength+1];
    
    // Read your file
    AAsset_read(file, fileContent, fileLength);
    // For safety you can add a 0 terminating character at the end of your file ...
    fileContent[fileLength] = '\0';
    
    // Do whatever you want with the content of the file
    
    // Free the memoery you allocated earlier
    delete [] fileContent;
    

你可以在这里查找官方NDK文档

编辑: 要获取AAssetManager对象:

  • 在本地活动中,您的主函数参数android_app* app,您只需要在此处获取:app->activity->assetManager
  • 如果您有一个Java活动,则需要通过JNI发送一个Java对象AssetManager的实例,然后使用函数AAssetManager_fromJava()

3

您的资源已打包到apk中,因此无法像您提供的示例代码一样在运行时直接引用它们。在这里您有两个选项:

  • 将资源作为输入流处理,使用

Context.getAssets().open('cacert.cert')

  • 将您的资源复制到本地文件中,并引用已复制文件的文件名。

第一个选项现在对我不起作用。 第二个选项呢?我应该将这个文件放到哪个位置,以便我能够从.so库中访问它? - Pavel S.
1
任何实际的文件系统位置 - 内部存储,外部存储等 - 显然具有所有通常的权衡。我相信还有从本地代码访问资产的方法,但它不仅仅是一个“文件”。 - Chris Stratton
不太清楚您的意思。如果您是指创建一个实际文件的副本,那么是的,您需要将原生代码手动传递一个路径或文件描述符。如果您是指资产本身,则没有路径,因为它不是一个文件(虽然仍然可以通过特殊方式访问)。 - Chris Stratton
实际上,您可以从NDK访问资产文件夹中的资产...(请参见下面我的答案) - Sistr
捆绑的资源没有文件路径。如果目标是在本地C/C++代码中对资产进行文件I/O操作,则主应用程序可以传播到JNI函数的唯一信息是此处的am变量 AssetManager am = this.getContext().getAssets();。 C/C++代码将接收此am变量并从那里继续工作。最终,C/C++中的文件I/O将具有此行:int nread = AAsset_read(asset,buf, length);。但在此之前,您需要这个:int fd = AAsset_openFileDescriptor(asset, &offset, &length);如果fd小于零,则表示资产已压缩。 - daparic
显示剩余3条评论

0

在 @Sistr 的回答基础上,我使用了 getBufferAASSET_MODE_BUFFER(而不是 AAsset_read)。

但是,我尝试将缓冲区写入 ofstream。使用 << 操作符 时,我遇到了信号崩溃:

ofstream myStream(<some file>, ios_base::binary);
auto buffer = AAsset_getBuffer(asset);
myStream << buffer;

我猜这是因为缓冲指针指向的资源末尾没有NULL字符(我正在阅读一个文本资源; Android源代码)。使用write函数可以解决:

ofstream myStream(<some file>, ios_base::binary);
auto buffer = AAsset_getBuffer(asset);
myStream.write((char *)buffer, AAsset_getLength(asset));

这是因为<<operator不需要调用strlen来找出要写入的字符数,因为它明确地由AAsset_getLength给出。

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