Android NDK 读写文件

本文最后更新于:2023年4月15日 下午

开发环境与工具: win7_x64,Android Studio 2.2.3, Cygwin

使用NDK,就进入了Linux的世界。理解了这一点,很多事情就好办了。需要熟悉C语言操作文件的方式。

准备事项

申请权限

申请SD卡的读写权限

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

6.0开始需要动态申请权限。

使用Linux中的文件绝对路径

手机sd卡中存放着文件。目录为 /sdcard/hello.txt/sdcard/egdir/csv/eg_data.csv

读写示例

文件目录

1
2
3
4
jni/
|-- Android.mk
|-- com_rustfisher_ndkalgo_RWFile.h
`-- RWFile.c

Java文件

RWFile.java 里面写了多个native方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class RWFile {

static {
System.loadLibrary("NDKMan");
}

public String readIn() {
return nativeRead();
}

public String writeToFile(String msg) {
return nativeWrite(msg);
}

public String readSDFile(String fileName) {
return nativeReadSDFile(fileName);
}

private native String nativeRead();

/**
* @param fileName e.g. FolderName/textFile.txt
*/
private native String nativeReadSDFile(String fileName);

private native String nativeWrite(String msg);
}

RWFile.c编译出.h文件后,里面的方法名。可以看出编译后的方法名和原来的native方法是一一对应的。

1
2
3
4
void join(char *s1, char *s2,char *result)          // 拼接char的函数
Java_com_rustfisher_ndkalgo_RWFile_nativeRead // 读取固定文件的内容
Java_com_rustfisher_ndkalgo_RWFile_nativeReadSDFile // 根据文件名读取文件内容
Java_com_rustfisher_ndkalgo_RWFile_nativeWrite // 将内容写入文件

C文件

C文件是我们具体实现功能的地方。RWFile.c代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <stdio.h>
#include <stdlib.h>

#include "com_rustfisher_ndkalgo_RWFile.h"

const char *sdcardDir = "/sdcard/";

void join(char *s1, char *s2,char *result) {
result = (char *) malloc(strlen(s1)+strlen(s2)+1);// +1 for the zero-terminator
if (result == NULL) {
return;
}
strcpy(result, s1);
strcat(result, s2);
}

JNIEXPORT jstring JNICALL Java_com_rustfisher_ndkalgo_RWFile_nativeRead
(JNIEnv *env, jobject jObj) {
FILE* file = fopen("/sdcard/hello.txt","r+");
char myStr[128];
if (file != NULL) {
char* readInPtr = fgets(myStr, 128, file);
fclose(file);
if (NULL != readInPtr) {
return (*env)->NewStringUTF(env, myStr);
}
return (*env)->NewStringUTF(env, "JNI read file fail!");
}
return (*env)->NewStringUTF(env, "JNI read file fail!");
}

JNIEXPORT jstring JNICALL Java_com_rustfisher_ndkalgo_RWFile_nativeReadSDFile
(JNIEnv *env, jobject jObj, jstring fileName) {
char *fileNamePtr = (*env)->GetStringUTFChars(env, fileName, 0);
char * result;
join(sdcardDir,fileNamePtr,result);

FILE* file = fopen(result,"r+");

if (file != NULL) {
char myStr[128];
char* readInPtr = fgets(myStr, 128, file);
fclose(file);
if (NULL != readInPtr) {
return (*env)->NewStringUTF(env, myStr);
}
return (*env)->NewStringUTF(env, "JNI read fail - NULL == readInPtr");
}
return (*env)->NewStringUTF(env, "JNI read file fail! - file is NULL ");
}


JNIEXPORT jstring JNICALL Java_com_rustfisher_ndkalgo_RWFile_nativeWrite
(JNIEnv *env, jobject jObj, jstring msg) {

FILE* file = fopen("/sdcard/hello.txt","w+");
const char *nativeMsg = (*env)->GetStringUTFChars(env, msg, 0);

if (file != NULL) {
fputs(nativeMsg, file);
fflush(file);
fclose(file);
}

return (*env)->NewStringUTF(env, "Write finished.");
}

需关注的函数

要特别关心函数的返回值,返回值往往代表着调用的结果。

打开文件 fopen

FILE * fopen(const char * path,const char * mode);
mode模式选择,例如"r"

  • r(read): 读
  • w(write): 写
  • a(append): 追加
  • t(text): 文本文件,可省略不写
  • b(banary): 二进制文件
  • +: 读和写

凡用“r”打开一个文件时,该文件必须已经存在,且只能从该文件读出。

用“w”打开的文件只能向该文件写入。若打开的文件不存在,则以指定的文件名建立该文件,若打开的文件已
经存在,则将该文件删去,重建一个新文件。这个方法保证目标文件里写入的只有我们要的数据。

若要向一个已存在的文件追加新的信息,只能用“a”方式打开文件。但此时该文件必须是存在的,否则将会出错。

在打开一个文件时,如果出错,fopen将返回一个空指针值NULL。在程序中可以用这一信息来判别是否完成
打开文件的工作,并作相应的处理。

如果成功的打开一个文件, fopen()函数返回文件指针, 否则返回空指针

从文件中读取数据 fgets

char *fgets(char *s, int n, FILE *stream);
从文件指针stream中读取n-1个字符,存到以s为起始地址的空间里,直到读完一行,如果成功则返回s的指
针,否则返回NULL。

NDK中生成jstring的函数 (*env)->NewStringUTF(env, char *);

(*env)->NewStringUTF(env, char *);
如果传入的char*是一个空值,在一些平台上会报错。
比如红米手机会直接崩溃,而魅族手机能得到一个空的String。


Android NDK 读写文件
https://blog.rustfisher.com/2017/03/16/Android/NDK-read_write_file/
作者
Rust Fisher
发布于
2017年3月16日
许可协议