6

我只能找到 2010 年或更早的解决方案。所以我想看看在这方面是否有更新的立场。

我想避免使用 Java 并纯粹使用 C++ 来访问存储在 APK 中的文件(一些小于或大于 1MB)。使用AssetManager意味着我无法像其他所有操作系统(包括 iOS)上的所有其他文件一样访问文件。

如果没有,C++ 中是否有一种方法可以将 fopen/fread 映射到 AssetManager API?

4

2 回答 2

12

实际上,我找到了这个问题的非常优雅的答案,并在这里写了博客

总结是:

  • AAssetManager API 具有 NDK 绑定。这使您可以从 APK 加载资产。
  • 可以组合一组知道如何读取/写入/查找任何内容的函数,并将它们伪装成文件指针 (FILE*)。
  • 如果我们创建一个获取资产名称的函数,使用 AssetManager 打开它,然后将结果伪装成 FILE*,那么我们就有了与 fopen 非常相似的东西。
  • 如果我们定义一个名为 fopen 的宏,我们可以用我们的函数替换该函数的所有使用。

我的博客有完整的文章和你需要用纯 C 实现的所有代码。我用它来为 Android 构建 lua 和 libogg。

于 2015-02-14T18:46:51.823 回答
6

简短的回答

不可以。AFAIK 将 C++ 中的 fread/fopen 映射到 AAssetManager 是不可能的。如果是的话,它可能会将您限制在资产文件夹中的文件中。然而,有一种解决方法,但这并不简单。

长答案

可以使用 C++ 中的 zlib 和libzip访问 APK 中任何位置的任何文件。要求:一些 java、zlib 和/或 libzip(为了便于使用,所以这就是我所接受的)。你可以在这里获取 libzip:http: //www.nih.at/libzip/

libzip 可能需要一些修补才能使其在 android 上运行,但没什么大不了的。

第 1 步:在 Java 中检索 APK 位置并传递给 JNI/C++

String PathToAPK;
ApplicationInfo appInfo = null;
PackageManager packMgmr = parent.getPackageManager();
try {
    appInfo = packMgmr.getApplicationInfo("com.your.application", 0);
} catch (NameNotFoundException e) {
    e.printStackTrace();
    throw new RuntimeException("Unable to locate APK...");
}

PathToAPK = appInfo.sourceDir;

将 PathToAPK 传递给 C++/JNI

JNIEXPORT jlong JNICALL Java_com_your_app(JNIEnv *env, jobject obj, jstring PathToAPK)
{
    // convert strings
    const char *apk_location = env->GetStringUTFChars(PathToAPK, 0);

    // Do some assigning, data init, whatever...
    // insert code here

    //release strings
    env->ReleaseStringUTFChars(PathToAPK, apk_location);

    return 0;
}

假设您现在有一个带有 APK 位置的 std::string 并且您在 libzip 上运行了 zlib,您可以执行以下操作:

if(apk_open == false)
{
    apk_file = zip_open(apk_location.c_str(), 0, NULL);

    if(apk_file == NULL)
    {
        LOGE("Error opening APK!");
        result = ASSET_APK_NOT_FOUND_ERROR;
    }else
    {
        apk_open = true;
        result = ASSET_NO_ERROR;
    }
}

并从 APK 中读取文件:

if(apk_file != NULL){
    // file you wish to read; **any** file from the APK, you're not limited to regular assets
    const char *file_name = "path/to/file.png";

    int file_index;
    zip_file *file;
    struct zip_stat file_stat;

    file_index = zip_name_locate(apk_file, file_name, 0);

    if(file_index == -1)
    {
        zip_close(apk_file);
        apk_open = false;

        return;
    }

    file = zip_fopen_index(apk_file, file_index, 0);
    if(file == NULL)
    {
        zip_close(apk_file);
        apk_open = false;

        return;
    }

    // get the file stats
    zip_stat_init(&file_stat);
    zip_stat(apk_file, file_name, 0, &file_stat);
    char *buffer = new char[file_stat.size];

    // read the file
    int result = zip_fread(file, buffer, file_stat.size);
    if(result == -1)
    {
        delete[] buffer;
        zip_fclose(file);

        zip_close(apk_file);
        apk_open = false;

        return;
    }

    // do something with the file
    // code goes here

    // delete the buffer, close the file and apk
    delete[] buffer;
    zip_fclose(file);

    zip_close(apk_file);
    apk_open = false;

不完全是 fopen/fread,但它完成了工作。将其包装到您自己的文件读取函数中以抽象 zip 层应该很容易。

于 2013-08-07T15:56:22.160 回答