fopen/fread APK来自Android上NativeActivity的资产

Mik*_*eir 6 c++ android android-ndk native-activity

我只能找到2010年及之前的解决方案.所以我想知道是否有更新的立场.

我想避免使用Java并纯粹使用C++来访问存储在APK中的文件(少于或多于1MB).使用AssetManager意味着我不能像其他操作系统(包括iOS)上的每个其他文件一样访问文件.

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

Bri*_*ian 11

我实际上找到了相当优雅的问题答案并在此博客.

摘要是:

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

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


Eri*_*rik 6

简短的回答

不可以.在C++中使用AFAIK映射fread/fopen到AAssetManager是不可能的.如果它可能会限制您在assets文件夹中的文件.然而,有一个解决方法,但它并不简单.

答案很长

IS可以使用zlib的和libzip在C++中的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;
Run Code Online (Sandbox Code Playgroud)

将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;
}
Run Code Online (Sandbox Code Playgroud)

假设您现在有一个带有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;
    }
}
Run Code Online (Sandbox Code Playgroud)

并从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;
Run Code Online (Sandbox Code Playgroud)

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