出于性能原因,我主要使用 Android NDK 在 C 中制作应用程序,但似乎 fopen 等文件操作在 Android 中无法正常工作。每当我尝试使用这些功能时,应用程序就会崩溃。
如何使用 Android NDK 创建/写入文件?
出于性能原因,我主要使用 Android NDK 在 C 中制作应用程序,但似乎 fopen 等文件操作在 Android 中无法正常工作。每当我尝试使用这些功能时,应用程序就会崩溃。
如何使用 Android NDK 创建/写入文件?
其他答案是正确的。FILE
您可以使用和通过 NDK 打开文件fopen
,但不要忘记为其设置权限。
在 Android 清单的地方:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
文件 IO 在使用 JNI 的 Android 上运行良好。也许您正试图打开一个路径错误的文件而不检查返回码?我修改了 hello-jni 示例以证明确实可以打开文件并对其进行写入。我希望这有帮助。
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <string.h>
#include <jni.h>
#include <stdio.h>
/* This is a trivial JNI example where we use a native method
* to return a new VM String. See the corresponding Java source
* file located at:
*
* apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java
*/
jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz )
{
FILE* file = fopen("/sdcard/hello.txt","w+");
if (file != NULL)
{
fputs("HELLO WORLD!\n", file);
fflush(file);
fclose(file);
}
return (*env)->NewStringUTF(env, "Hello from JNI (with file io)!");
}
这是在我的手机(使用 SD 卡)上运行后的结果:
$ adb -d shell cat /sdcard/hello.txt
HELLO WORLD!
确保使用 JavagetExternalStorageDirectory()
调用来获取 sdcard 的真实路径,因为较新的设备不会简单地将其映射到“/sdcard”。在这种情况下,尝试使用“/sdcard”的硬编码位置将失败。
我还可以验证 fopen() 是否正常工作,但如果您尝试访问应用程序的资源或资产文件夹中的文件,则不能。为了避免重新发明轮子,我建议您将任何想要随应用程序一起提供的资产粘贴在 assets 文件夹中,它们将被打包以进行分发。
在资产文件夹的情况下,您需要执行以下两项操作之一,具体取决于文件是否由打包程序压缩。两者都使用 AssetManager 方法,您可以从上下文/应用程序中获取 AssetManager。文件名总是相对于资产文件夹,顺便说一句:如果您在资产文件夹中直接有一个文件“foo.png”,那么您将打开“foo.png”,而不是“assets/foo.png”之类的东西。
如果文件未压缩(即,它是未压缩的扩展名之一,如 .png),您可以从 AssetManager.openFd() 获取文件描述符并将其传递给 C++。然后你可以使用 fdopen(dup(fd),"r"); 将文件作为 FILE* 打开。请注意,您必须 fseek() 到偏移量,并自己跟踪文件的长度。您确实获得了整个资产包的文件句柄,而您感兴趣的文件只是其中的一小部分。
如果您的文件被压缩,则需要使用 Java 流式阅读器:AssetManager.open() 为您提供了一个 InputStream,您可以使用它来读取文件。这是一个 PITA,因为您无法查询 (AFAIK) 文件大小;我在我的资产文件夹上运行了一个预处理步骤,该步骤生成所有文件的列表以及它们各自的大小,这样我就可以知道要分配多大的缓冲区。
如果您的文件是资源,您可能需要通过 Resource 类来访问它,尽管看起来资源也被打包到同一个资产包中。Resource 有一个 openRawResource() 调用来获取 InputStream 和一个 openRawResourceFd() 调用来获取文件描述符,如上所述。
祝你好运。
我想在此处的答案中添加两分钱,除了设置此问题的答案中指定的正确权限之外,请确保授予您的应用访问操作系统存储的权限。权限菜单可能会因手机而异,一个简单的方法是转到“设置”菜单,然后搜索“权限”。这将使您有机会授予您的应用程序从 NDK 代码访问存储(即 sdcard 目录)的权限。