【问题标题】:How To Get File In Assets From Android NDK如何从 Android NDK 获取资产中的文件
【发布时间】:2012-10-30 07:44:42
【问题描述】:

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

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));

但该文件名也必须包含绝对路径,因为它不在同一目录中。但是我发现我无法获取资产目录的绝对路径,因为它是与 APK 一起压缩的。

由于我无法访问绝对路径,我可以使用什么其他方式来获取图像?另外需要注意的是,我确实希望将资产文件夹中的文件与 sd 卡保持一致。

【问题讨论】:

  • 我无法理解 - 如何在 c++ ndk 代码中访问 asset_manager 变量?
  • android_app->activity->asset_manager

标签: android android-ndk openscenegraph


【解决方案1】:

您可以使用 AAssetManager_openAAsset_read 从资产中读取图像,但由于资产位于 apk 中,您无法获取它的文件路径名 - 它也在此处被压缩。您可以将数据保存到文件中并稍后从该文件中读取,或者如果 OSG 允许,您可以直接处理从资产文件中获得的块。

来自here

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);

【讨论】:

  • 感谢您的快速响应。我会尝试一下,并在可能的时候给你更新。
  • 抱歉更新晚了(忙了一周)。这将正常工作。这是其他应用程序通常如何访问其不在 SD 卡中的文件的方式吗?
  • @Robert 不知道其他应用程序,但这看起来没问题。
  • 请注意,使用此方法您将在磁盘中提取 APK 的内容。根用户将能够访问您的应用程序的资产,所以要小心。 osg 没有从数据中读取图像的方法吗?您应该使用 AssetManager 加载数据并从数据中加载图像。
  • @user2591935,所有应用都可以访问资产。
【解决方案2】:

还有一点值得一提的是,android 将可读取的资源大小限制为 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 MB 可能不是架构的最佳选择。如果更小的块大小(如 512 KB)完全适合缓存,则读取效率可能更高。
  • 如果您正在阅读整个文件,为什么不使用AASSET_MODE_BUFFERAAsset_getBuffer() 像这样:stackoverflow.com/a/33957074/673679
  • 读取整个文件的概念很幼稚。块头,永远是最好的。但我同意AASSET_MODE_BUFFER
  • @typelogic 平台使用StreamingZipInflater,块大小(截至今天的主节点)为 64Kbyte。这个大小是一个实现细节,可能会因不同的 Android 版本或不同的设备而改变(OEM 有充分的权力来调整他们的常量以更好地适应他们的硬件)。如果你使用AASSET_MODE_BUFFERgetBuffer() 会比任何块都好,因为在后台,平台可以一次性加载所有内容。
  • 我的实现是类似的。但是,对于具有较新 android 版本(例如 10+)的设备,我的解决方案无法正常工作。我现在将切换到AASSET_MODE_BUFFER。在android 10+上还有其他人这样的问题
猜你喜欢
  • 1970-01-01
  • 2014-06-15
  • 2012-05-11
  • 2011-01-17
  • 1970-01-01
  • 2014-03-10
  • 2011-06-16
  • 2014-02-14
相关资源
最近更新 更多