如何从Android NDK获取assets文件

44

我正在尝试从本地访问资产文件夹中的图像文件。现在,我可以成功地搜索资产文件夹及其子目录,找到我要查找的特定文件:

AAssetDir* assetDir = AAssetManager_openDir(asset_manager, "images");
const char* filename;
while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL)
{
   __android_log_print(ANDROID_LOG_DEBUG, "Debug", filename);
}

AAssetDir_close(assetDir);

我也在使用OSG。我使用以下代码将图像设置为我的着色器的纹理:

texture->setImage(osgDB::readImageFile(fileNameWithPath));

但是由于该文件并不在同一目录下,因此文件名必须包括绝对路径。然而,我发现我无法获取 assets 目录的绝对路径,因为它与 APK 一起压缩。

既然我无法访问绝对路径,我还能用什么方法获取图片呢?需要注意的另一点是,我确实想将文件保留在 asset 文件夹中,而不是 SD 卡中。


1
我不明白 - 在C++ NDK代码中如何访问"asset_manager"变量? - fnc12
1
安卓应用程序->活动->资产管理器 - kaiser
2个回答

43

你可以使用AAssetManager_openAAsset_read从资源文件中读取图像,但是由于资源文件在apk中,你无法获取它的文件路径 - 它也被压缩在那里。你可以将数据保存到文件中以后再从该文件中读取,或者如果OSG允许,直接处理从资源文件获取的块。

来自这里

AAssetDir* assetDir = AAssetManager_openDir(mgr, "");
const char* filename = (const char*)NULL;
while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL) {
    AAsset* asset = AAssetManager_open(mgr, filename, AASSET_MODE_STREAMING);
    char buf[BUFSIZ];
    int nb_read = 0;
    FILE* out = fopen(filename, "w");
    while ((nb_read = AAsset_read(asset, buf, BUFSIZ)) > 0)
        fwrite(buf, nb_read, 1, out);
    fclose(out);
    AAsset_close(asset);
}
AAssetDir_close(assetDir);

谢谢您的快速回复。我会尝试这个方法并在可以时给您更新。 - Dyn0myte
抱歉更新晚了(这周很忙)。这个可以正常工作。其他应用程序通常是通过这种方式访问不在SD卡中的文件吗? - Dyn0myte
@Robert 我不知道其他应用程序,但这看起来还不错。 - auselen
请注意,使用此方法将从您的磁盘中提取APK的内容。已获取Root权限的用户将能够访问您的应用程序资产,请小心。OSG是否有一种从数据中读取图像的方法?您应该使用AssetManager加载数据并从数据中加载图像。 - user2591935
@user2591935,资产对所有应用程序都是可访问的。 - jiwopene
但是我怎么才能得到'mgr'呢? - Aaron Lee

1

还有一件值得一提的事情是,安卓对于可以一次读取的资产大小有限制,为1Mb。如果您的文件超过了这个大小,那么您就需要将其拆分成块。这是我的工作解决方案,它将文件按块加载到字符向量中:

AAssetManager * mgr = app->activity->assetManager; 
AAssetDir* assetDir = AAssetManager_openDir(mgr, "");

const char* filename;
std::vector<char> buffer;

while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL) 
{
    //search for desired file
    if(!strcmp(filename, /* searched file name */)) 
    {
        AAsset *asset = AAssetManager_open(mgr, filename, AASSET_MODE_STREAMING);

        //holds size of searched file
        off64_t length = AAsset_getLength64(asset);
        //keeps track of remaining bytes to read
        off64_t remaining = AAsset_getRemainingLength64(asset);
        size_t Mb = 1000 *1024; // 1Mb is maximum chunk size for compressed assets
        size_t currChunk;
        buffer.reserve(length);

        //while we have still some data to read
        while (remaining != 0) 
        {
            //set proper size for our next chunk
            if(remaining >= Mb)
            {
                currChunk = Mb;
            }
            else
            {
                currChunk = remaining;
            }
            char chunk[currChunk];

            //read data chunk
            if(AAsset_read(asset, chunk, currChunk) > 0) // returns less than 0 on error
            {
                //and append it to our vector
                buffer.insert(buffer.end(),chunk, chunk + currChunk);
                remaining = AAsset_getRemainingLength64(asset);
            }
        }
        AAsset_close(asset);
    }

}

1
请注意,1 MB 不一定是最合适的架构。如果可以完全适配缓存,较小的块大小如 512 KB 可能更有效地读取。 - Dan
1
如果您正在读取整个文件,为什么不像这样使用AASSET_MODE_BUFFERAAsset_getBuffer():https://dev59.com/OWAg5IYBdhLWcg3wo8L2#33957074 - user673679
“读取整个文件”的概念是幼稚的。分块始终是最好的选择。但我同意使用AASSET_MODE_BUFFER - daparic
@typelogic 平台使用 StreamingZipInflater,块大小为64Kbyte(截至今天的主干)。这个大小是一个实现细节,可能会因不同的Android版本或不同的设备而改变(OEM有完全的权力调整他们的常量以更好地适应他们的硬件)。如果您使用 AASSET_MODE_BUFFER,则 getBuffer() 将比任何块都要好,因为在底层,平台一次性加载所有内容。 - Alex Cohn
我的实现方式类似,但是在新版本的安卓设备上(例如10+),我的解决方案无法正常工作。我现在将切换到“ AASSET_MODE_BUFFER”。还有其他人在安卓10+上遇到这样的问题吗? - seb

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