使用 NDK 在 Android 中创建临时文件

发布于 2024-12-06 21:03:57 字数 552 浏览 1 评论 0原文

我目前正在开发一个基于 C、基于 NDK 的 Android 应用程序。该应用程序需要创建临时文件。在常规 Linux 系统上,我会使用 tmpfile 来确保这些文件在临时目录中正确创建并在进程结束时清理。

然而,我对各种 Android 设备的调查似乎表明

  • tmpfile 总是失败;
  • 没有 /tmp 目录;
  • 目录 /data/local/tmp 并不存在于所有 Android 变体上;
  • 没有设置TEMP环境变量;
  • mkstemp 并不比 tmpfile 更好。

现在,我确信我可以一起破解一些东西,但是看到 SDK 为 Java 应用程序提供了 context.getCacheDirFile.createTempFile,我希望有一个相当于C级。

有谁知道创建临时文件的可靠且跨Android的好方法?

I am currently working on a C-based, NDK-based, Android application. This application needs to create temporary files. On a regular Linux system, I would use tmpfile to ensure that these files are properly created in a temporary directory and cleaned-up at process end.

However, my investigations on various Android devices seem to indicate that

  • tmpfile always fails;
  • there is no /tmp directory;
  • directory /data/local/tmp isn't present on all variants of Android;
  • there is no TEMP environment variable set;
  • mkstemp doesn't work any better than tmpfile.

Now, I'm sure that I could hack something together, but seeing that the SDK offers context.getCacheDir and File.createTempFile for Java applications, I hope that there is an equivalent at C-level.

Does anyone know of a good reliable and cross-Android method for creating a temporary file?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

不寐倦长更 2024-12-13 21:03:57

我们发现的最好方法是在启动时调用 Context.getCacheDir,使用 getAbsolutePath 获取其路径,然后调用 JNI 函数将该路径存储在全局中。任何想要创建临时文件的函数只需将合适的临时文件名附加到该路径即可。

如果您确实想从 JNI 获取它,另一种选择是将 Context 传递给 JNI 函数并使用一堆 GetMethodID / CallObjectMethod 回调到 Java 中的 getCacheDir 的内容,但前一种方法要简单得多。

不幸的是,目前似乎没有更优雅的解决方案。

The best way we've found is to call Context.getCacheDir on startup, get its path with getAbsolutePath, then call a JNI function to store that path in a global. Any function that wants to create a temporary file simply appends a suitable temporary file name to that path.

If you really want to fetch it from JNI another alternative would be to pass in a Context to a JNI function and use a bunch of GetMethodID / CallObjectMethod stuff to call back into Java to getCacheDir, but the former approach is a lot simpler.

Unfortunately, there does not appear to be a more elegant solution at the moment.

征﹌骨岁月お 2024-12-13 21:03:57

下面是 Ertebolle 引用的 GetMethodID / CallObjectMethod 过程。如果您使用纯本机应用程序(例如由 Visual Studio 2015 构建)并且无法使用 java 代码,则这是必要的。

std::string android_temp_folder( struct android_app *app ) {
    JNIEnv* env;
    app->activity->vm->AttachCurrentThread( &env, NULL );

    jclass activityClass = env->FindClass( "android/app/NativeActivity" );
    jmethodID getCacheDir = env->GetMethodID( activityClass, "getCacheDir", "()Ljava/io/File;" );
    jobject cache_dir = env->CallObjectMethod( app->activity->clazz, getCacheDir );

    jclass fileClass = env->FindClass( "java/io/File" );
    jmethodID getPath = env->GetMethodID( fileClass, "getPath", "()Ljava/lang/String;" );
    jstring path_string = (jstring)env->CallObjectMethod( cache_dir, getPath );

    const char *path_chars = env->GetStringUTFChars( path_string, NULL );
    std::string temp_folder( path_chars );

    env->ReleaseStringUTFChars( path_string, path_chars );
    app->activity->vm->DetachCurrentThread();
    return temp_folder;
}

Below is the GetMethodID / CallObjectMethod procedure that Ertebolle refers to. It is necessary if you are working with a pure native app (such as built by Visual Studio 2015) and cannot use java code.

std::string android_temp_folder( struct android_app *app ) {
    JNIEnv* env;
    app->activity->vm->AttachCurrentThread( &env, NULL );

    jclass activityClass = env->FindClass( "android/app/NativeActivity" );
    jmethodID getCacheDir = env->GetMethodID( activityClass, "getCacheDir", "()Ljava/io/File;" );
    jobject cache_dir = env->CallObjectMethod( app->activity->clazz, getCacheDir );

    jclass fileClass = env->FindClass( "java/io/File" );
    jmethodID getPath = env->GetMethodID( fileClass, "getPath", "()Ljava/lang/String;" );
    jstring path_string = (jstring)env->CallObjectMethod( cache_dir, getPath );

    const char *path_chars = env->GetStringUTFChars( path_string, NULL );
    std::string temp_folder( path_chars );

    env->ReleaseStringUTFChars( path_string, path_chars );
    app->activity->vm->DetachCurrentThread();
    return temp_folder;
}
悲念泪 2024-12-13 21:03:57

据我所知,android中没有全局/tmp,你应该使用缓存目录。使用 getCacheDir() 获取“tmp”目录。

http://developer.android.com/guide/topics/data /data-storage.html#filesInternalhttp://developer.android.com/reference/android/content/Context.html#getCacheDir%28%29

As far as I know there is no global /tmp in android, you should use the cache dir. Use getCacheDir() to get the "tmp" dir.

http://developer.android.com/guide/topics/data/data-storage.html#filesInternal and http://developer.android.com/reference/android/content/Context.html#getCacheDir%28%29

夜光 2024-12-13 21:03:57

mkstemp 在 NDK 的 stdlib.h 下可用

mkstemp is available in the NDK under stdlib.h

别理我 2024-12-13 21:03:57
  1. 使用 ContentProvider 在应用程序启动时获取缓存目录的路径。
  2. 实现 tmpfile 函数,其签名与 POSIX 的 tmpfile 相同。
  3. 使用之前获得的缓存目录从 tmpfile 函数中调用 mkstemp 将

其打包为 .aar,以便可以通过 gradle 使用。

https://github.com/ViliusSutkus89/tmpfile-Android

  1. Obtain path to cache directory on application's startup using a ContentProvider.
  2. Implement tmpfile function with the same signature as POSIX's tmpfile.
  3. Call mkstemp from your tmpfile function using the previously obtained cache directory

Packaged it as .aar, so it could be consumed through gradle.

https://github.com/ViliusSutkus89/tmpfile-Android

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文