【问题标题】:fopen/fread APK Assets from NativeActivity on Android从 Android 上的 NativeActivity fopen/fread APK 资产
【发布时间】:2013-08-08 01:37:51
【问题描述】:

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

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

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

【问题讨论】:

  • 由于资产不是文件,答案很可能是“否”。资产是 ZIP 存档中的条目,即 APK。
  • 从技术上讲,由于 APK 只不过是一个精美的 zip 文件,因此完全可以访问 APK 中的任何文件,就像它是 c++ 中的 zip 文件一样。然而,它确实需要 some java,但只是为了获取您的 apk 安装位置。我知道事实重读不是问题,但我从未尝试过写入 APK。

标签: android c++ android-ndk native-activity


【解决方案1】:

我实际上找到了这个问题的非常优雅的答案和blogged about it here

总结是:

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

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

【讨论】:

  • 文件大小:size_t file_size (FILE *fp) { android_seek(fp, 0L, SEEK_END); size_t sz = ftell(fp); android_seek(fp, 0L, SEEK_SET); return sz; }
【解决方案2】:

简答

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

长答案

IS可以使用 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 层应该很容易。

【讨论】:

  • libz 现在是由 Android 工具链提供的——在 libzip 上使用它会有什么危险吗?它们甚至不同/相同吗?
  • 完全没有。我也使用 android 提供的 libz; libzip 只是一个位于 libz 之上的库,使阅读/写作变得更容易。
  • 一个快速修复,你需要 delete[] 而不是在缓冲区上删除。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-03
  • 1970-01-01
  • 2011-05-21
  • 1970-01-01
  • 2015-01-22
相关资源
最近更新 更多