Android - 仅从本机代码写入/保存文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11294487/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 06:48:02  来源:igfitidea点击:

Android - writing/saving files from native code only

androidandroid-ndk

提问by celavek

I'm trying to build an Android app which makes use of the NativeActivityfacility of the NDK. I'm having the following structure:

我正在尝试构建一个利用NativeActivityNDK 功能的 Android 应用程序。我有以下结构:

  • a bunch of native shared libraries installed in /system/vendor/<company>; I'm working with a custom built Android image so there's no problem having the libraries there with proper permissions and everything
  • a couple of applications using the NativeActivitythat depend in turn on the libraries mentioned above
  • 安装了一堆本地共享库/system/vendor/<company>;我正在使用自定义构建的 Android 映像,因此在那里拥有具有适当权限的库和所有内容都没有问题
  • 使用 的几个应用程序NativeActivity依次依赖于上述库

The libraries installed in the /system/vendor and my applications use a couple of configuration files. There's no problem reading them using the standard C API fopen/fclose. But those libraries and my application also need to store some files as the result of their operation, like configuration, some run-time parameters, calibration data, log files etc. With the storing of the files there is a slight issue as I'm not allowed to write into /system/vendor/...(as the file system under "/system/..." is mounted read-only and I do not want to hack on that).

安装在 /system/vendor 和我的应用程序中的库使用了几个配置文件。使用标准 C API 阅读它们没有问题 fopen/fclose。但是这些库和我的应用程序还需要存储一些文件作为其操作的结果,如配置、一些运行时参数、校准数据、日志文件等。 文件的存储有一个小问题,因为我不允许写入/system/vendor/...(因为“/system/...”下的文件系统以只读方式安装,我不想对此进行修改)。

So what would be the best way to create and store those files and where would be the best "conforming with Android" storage area ?

那么创建和存储这些文件的最佳方式是什么,最好的“符合 Android”的存储区域在哪里?

I've been reading a couple of threads in the android-ndk Google group and here on SO that mention either the internal application private storageor the external SD card, but as I do not have extended experience with Android I'm not sure what would be the proper approach. If the approach involves some specific Android API a small code example in C++ would be very helpful; I've seen a couple of examples involving Java and JNI (e.g. in this SO question) but I would like to stay away from that right now. Also there seems to be a problem with using from C++ the native activity's internalDataPath/externalDataPathpair (a bug that makes them be always NULL).

我一直在阅读 android-ndk Google 组和这里的几个线程,其中提到了内部应用程序私有存储外部 SD 卡,但由于我没有使用 Android 的扩展经验,我不确定是什么将是正确的方法。如果该方法涉及某些特定的 Android API,则使用 C++ 编写的小代码示例将非常有帮助;我已经看到了几个涉及 Java 和 JNI 的例子(例如在这个 SO question 中),但我现在想远离那个。从 C++ 使用本机活动的internalDataPath/externalDataPath对似乎也存在问题 (一个使它们始终为 NULL 的错误)。

回答by celavek

For relatively small files(application config files, parameter files, log files etc.) is best to use the internal application private storage, that is /data/data/<package>/files. The external storage if it exists at all (being it SD card or not) should be used for large files that do not need frequent access or updates.

对于相对较小的文件(应用程序配置文件、参数文件、日志文件等),最好使用内部应用程序私有存储,即/data/data/<package>/files. 外部存储(无论是否存在 SD 卡)应该用于不需要频繁访问或更新的大文件。

For the external data storage the native application has to "request" the correct permissions in the application's AndroidManifest.xml:

对于外部数据存储,本机应用程序必须“请求”应用程序的正确权限AndroidManifest.xml

<manifest>
    ... 
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
    </uses-permission>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"> 
    </uses-permission>
</manifest>  

For the internal application private storage fopen/fclose(or C++ stream equivalents if available) API could be used. Following example illustrates using the Android NDK AssetManagerto retrieve and read a configuration file. The file must be placed into the assetsdirectory inside the native application's project folder so that the NDK build could pack them inside the APK. The internalDataPath/externalDataPathbug I was mentioning in the question was fixed for the NDK r8 version.

对于内部应用程序私有存储fopen/fclose(或 C++ 流等效项,如果可用)可以使用 API。以下示例说明使用 Android NDKAssetManager检索和读取配置文件。该文件必须放在assets本机应用程序的项目文件夹内的目录中,以便 NDK 构建可以将它们打包到 APK 中。internalDataPath/externalDataPath我在问题中提到的错误已针对 NDK r8 版本修复。

...
void android_main(struct android_app* state)
{
    // Make sure glue isn't stripped 
    app_dummy();

    ANativeActivity* nativeActivity = state->activity;                              
    const char* internalPath = nativeActivity->internalDataPath;
    std::string dataPath(internalPath);                               
    // internalDataPath points directly to the files/ directory                                  
    std::string configFile = dataPath + "/app_config.xml";

    // sometimes if this is the first time we run the app 
    // then we need to create the internal storage "files" directory
    struct stat sb;
    int32_t res = stat(dataPath.c_str(), &sb);
    if (0 == res && sb.st_mode & S_IFDIR)
    {
        LOGD("'files/' dir already in app's internal data storage.");
    }
    else if (ENOENT == errno)
    {
        res = mkdir(dataPath.c_str(), 0770);
    }

    if (0 == res)
    {
        // test to see if the config file is already present
        res = stat(configFile.c_str(), &sb);
        if (0 == res && sb.st_mode & S_IFREG)
        {
            LOGI("Application config file already present");
        }
        else
        {
            LOGI("Application config file does not exist. Creating it ...");
            // read our application config file from the assets inside the apk
            // save the config file contents in the application's internal storage
            LOGD("Reading config file using the asset manager.\n");

            AAssetManager* assetManager = nativeActivity->assetManager;
            AAsset* configFileAsset = AAssetManager_open(assetManager, "app_config.xml", AASSET_MODE_BUFFER);
            const void* configData = AAsset_getBuffer(configFileAsset);
            const off_t configLen = AAsset_getLength(configFileAsset);
            FILE* appConfigFile = std::fopen(configFile.c_str(), "w+");
            if (NULL == appConfigFile)
            {
                LOGE("Could not create app configuration file.\n");
            }
            else
            {
                LOGI("App config file created successfully. Writing config data ...\n");
                res = std::fwrite(configData, sizeof(char), configLen, appConfigFile);
                if (configLen != res)
                {
                    LOGE("Error generating app configuration file.\n");
                }
            }
            std::fclose(appConfigFile);
            AAsset_close(configFileAsset);
        }
    }
}