Skip to content

NDK 读写文本文件

开发环境与工具

  • Android Studio 4.1.1
  • NDK 20.0.*

用cpp代码实现文件的读写操作。

ndk环境搭建

main/cpp目录下的CMakeLists.txt,指定fisher-lib.cpp为源文件。库的名称定为fisher-lib

cmake_minimum_required(VERSION 3.4.1)
add_library( # Specifies the name of the library.
             fisher-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             fisher-lib.cpp )

Java代码

新建FileUtils文件,注意包名。

package com.rustfisher.tutorial2020.ndk.util;

/**
 * 文件操作功能
 * 2021-1-26
 */
public class FileUtils {

    static {
        System.loadLibrary("fisher-lib");
    }

    /**
     * @param filepath 文件绝对路径
     */
    public native String nReadFile(String filepath);

    public native void nWrite(String filepath, String msg);
}

记得加载fisher-lib

cpp代码

写好Java代码后,使用as的功能,可以很快的在cpp文件中得到方法名。

#include <jni.h>
#include <string>

extern "C"
JNIEXPORT jstring JNICALL
Java_com_rustfisher_tutorial2020_ndk_util_FileUtils_nReadFile(JNIEnv *env, jobject thiz,
                                                              jstring filepath) {
    char *filepathPtr = const_cast<char *>(env->GetStringUTFChars(filepath, JNI_FALSE));
    FILE *file = fopen(filepathPtr, "r+");
    char myStr[128];
    if (file != nullptr) {
        char *readInPtr = fgets(myStr, 128, file);
        fclose(file);
        if (nullptr != readInPtr) {
            return env->NewStringUTF(myStr);
        }
        return env->NewStringUTF("JNI read file fail!");
    }
    return env->NewStringUTF("JNI read file fail!");
}

extern "C"
JNIEXPORT void JNICALL
Java_com_rustfisher_tutorial2020_ndk_util_FileUtils_nWrite(JNIEnv *env, jobject thiz,
                                                           jstring filepath, jstring msg) {
    char *filepathPtr = const_cast<char *>(env->GetStringUTFChars(filepath, NULL));
    FILE *file = fopen(filepathPtr, "w+");
    const char *nativeMsg = reinterpret_cast<const char *>(env->GetStringUTFChars(msg, NULL));

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

运行测试

例如在NdkFileVm里执行操作

    private final FileUtils mNdkFileUtils = new FileUtils();
    String mFilepath1;

    public NdkFileVm(@NonNull Application application) {
        super(application);

        File file = new File(application.getFilesDir(), "ndk_file1.txt");

        mFilepath1 = file.getAbsolutePath();
        if (file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    public void writeInData(View v) {
        String data = "Rust Fisher. Data time " + System.currentTimeMillis();
        mNdkFileUtils.nWrite(mFilepath1, data);
    }

    public void readData(View v) {
        String data = mNdkFileUtils.nReadFile(mFilepath1);
        mObData1.set(data);
    }

方法记录

打开文件 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。

env->NewStringUTF

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

参考工程Tutorial2020