在上一篇中提及到了c回调java整体流程的实现,该实现中只是一个简单的示例,现在想以一个完整的文件读写流程进行c回调java的实现又该如何书写呢?
首先看一下完整的文件读写流程和上一个例子中简单的单函数调用有什么区别:
1. 简单的单函数调用中jni变量都是局部的,只在本次函数调用中有效;
2. 文件读写流程中会至少用到三个函数:open->read/write->close;即read/write进行操作时是依赖open返回的fd,这样jni局部变量就不能满足需求了;
首先,创建实现文件读写功能的java工具类 FileHelper
package com.sun.android.ccdemo;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.Map;
public class FileHelper {
private int mFd;
private Map mFd2RafMap;
private Map mRafFp;
public FileHelper() {
mFd = 3;
mFd2RafMap = new HashMap<>();
mRafFp = new HashMap<>();
}
public int open(String path, String mode) {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(path, mode);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
}
int fd = mFd;
mFd2RafMap.put(mFd++, raf);
mRafFp.put(raf, 0L);
return fd;
}
public int read(int fd, byte[] buf, int byteCount) {
RandomAccessFile raf = mFd2RafMap.get(fd);
if (null == raf)
return -1;
long fp = mRafFp.get(raf);
int count = 0;
try {
count = raf.read(buf, 0, byteCount);
} catch (IOException e) {
e.printStackTrace();
}
fp += Math.max(count, 0);
mRafFp.put(raf, fp);
return count;
}
public int write(int fd, byte[] buf, int byteCount) {
RandomAccessFile raf = mFd2RafMap.get(fd);
if (null == raf)
return -1;
long fp = mRafFp.get(raf);
try {
raf.write(buf, 0, byteCount);
} catch (IOException e) {
e.printStackTrace();
}
fp += byteCount;
mRafFp.put(raf, fp);
return byteCount;
}
public int close(int fd) {
RandomAccessFile raf = mFd2RafMap.get(fd);
if (null == raf)
return -1;
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
}
创建替换库函数的.h文件
// // Created by crow on 2022/6/1. // #ifndef CCDEMO_TEST_H #define CCDEMO_TEST_H #include#define fopen test_open #define fclose test_close #define fread test_read #define fwrite test_write #endif //CCDEMO_TEST_H
将MainActivity作为测试的入口,新建读写相关的native方法,在native-lib.c中实现。
MainActivity相关代码
private void readFile() {
String path = "/storage/emulated/0/document/example2.txt";
directReadFile(path);
int fd = openFile(path, "rw");
byte[] buff = new byte[30];
int readSize = readFile(fd, buff, buff.length);
Log.e("crow", "readFile: readSize "+ readSize);
try {
Log.e("crow", new String(buff, "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String str = "Tupperware";
byte[] datas = str.getBytes();
int writeSize = writeFile(fd, datas, datas.length);
Log.e("crow", "writeFile: writeSize "+ writeSize);
int closed = closeFile(fd);
Log.e("crow", "readFile: closed "+ closed);
}
private void directReadFile(String path) {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(path, "r");
byte[] buff = new byte[30];
raf.read(buff, 0, buff.length);
Log.e("crow", "direct read " + new String(buff, "UTF-8"));
} catch (IOException e) {
} finally {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public native int openFile(String fileName, String mode);
public native int readFile(int fd, byte[] buff, int count);
public native int writeFile(int fd, byte[] buff, int byteCount);
public native int closeFile(int fd);
native-lib.c的实现,native-lib.c中引入test.h,并调用库函数(此时的库函数已经做了替换,实际上调用的是test.h中对应的test_*方法)
#include#include #include #include "test.h" JNIEXPORT jint JNICALL Java_com_sun_android_ccdemo_MainActivity_openFile(JNIEnv *env, jobject thiz, jstring file_name, jstring access_mode) { const char *fileName = (*env)->GetStringUTFChars(env, file_name, 0); const char *accessMode = (*env)->GetStringUTFChars(env, access_mode, 0); jint result = fopen(fileName, accessMode); (*env)->ReleaseStringUTFChars(env, file_name, fileName); (*env)->ReleaseStringUTFChars(env, access_mode, accessMode); return result; } JNIEXPORT jint JNICALL Java_com_sun_android_ccdemo_MainActivity_readFile(JNIEnv *env, jobject thiz, jint fd, jbyteArray buff, jint count) { jbyte *bytes = NULL; bytes = (*env)->GetByteArrayElements(env, buff, NULL); // 读的时候给了bytes jint result = fread(fd, bytes, count); // 从bytes返回给buff (*env)->SetByteArrayRegion(env, buff, 0, count, bytes); (*env)->ReleaseByteArrayElements(env, buff, bytes, 0); return result; } JNIEXPORT jint JNICALL Java_com_sun_android_ccdemo_MainActivity_closeFile(JNIEnv *env, jobject thiz, jint fd) { return fclose(fd); } JNIEXPORT jint JNICALL Java_com_sun_android_ccdemo_MainActivity_writeFile(JNIEnv *env, jobject thiz, jint fd, jbyteArray buff, jint count) { jbyte *bytes = NULL; bytes = (*env)->GetByteArrayElements(env, buff, 0); jint result = fwrite(fd, bytes, count); (*env)->ReleaseByteArrayElements(env, buff, bytes, 0); return result; }
test.h 替换函数的实现 test.c, test.c通过jni去调用实现文件读写功能的java工具类 FileHelper
// // Created by crow on 2022/6/1. // #include "test.h" #include#include #include #include #include #include JavaVM *javaVm; jclass fileHelperClz; jobject fileHelper; jstring charToJstring(JNIEnv *env, const char *pat) { jsize len = strlen(pat); jclass strClass = (*env)->FindClass(env, "java/lang/String"); jstring encoding = (*env)->NewStringUTF(env, "utf-8"); jmethodID jmethodId = (*env)->GetMethodID(env, strClass, " ","([BLjava/lang/String;)V"); jbyteArray bytes = (*env)->NewByteArray(env, len); (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte *) pat); return (jstring) (*env)->NewObject(env, strClass, jmethodId, bytes, encoding); } jobject getFileHelper() { if (NULL != fileHelper) { return fileHelper; } JNIEnv *env = NULL; if ((*javaVm)->AttachCurrentThread(javaVm, &env, NULL) != JNI_OK) { return NULL; } if (fileHelperClz == NULL) { jclass localCls = (*env)->FindClass(env, "com/sun/android/ccdemo/FileHelper"); if (localCls == NULL) { return NULL; } fileHelperClz = (*env)->NewGlobalRef(env, localCls); (*env)->DeleteLocalRef(env, localCls); } if (NULL == fileHelperClz) { return NULL; } jmethodID initFieldId = (*env)->GetMethodID(env, fileHelperClz, " ", "()V"); if (NULL == initFieldId) return NULL; // 一次native方法有效 jobject localFileHelper = (*env)->NewObject(env, fileHelperClz, initFieldId); if (localFileHelper == NULL) { return NULL; } fileHelper = (*env)->NewGlobalRef(env, localFileHelper); (*env)->DeleteLocalRef(env, localFileHelper); return fileHelper; } jint test_open(const char* filename, const char* mode) { JNIEnv *env = NULL; if ((*javaVm)->AttachCurrentThread(javaVm, &env, NULL) != JNI_OK) { return NULL; } jobject fileHelperObj = getFileHelper(); if (fileHelperObj == NULL) { return -1; } if (NULL == fileHelperClz) { return -1; } jstring jFileName = charToJstring(env, filename); jstring jMode = charToJstring(env, mode); jmethodID openId = (*env)->GetMethodID(env, fileHelperClz, "open", "(Ljava/lang/String;Ljava/lang/String;)I"); return (*env)->CallIntMethod(env, fileHelperObj, openId, jFileName, jMode); } jbyteArray char2JByteArray(JNIEnv *env, char *buf, int len) { jbyteArray bytes = (*env)->NewByteArray(env, len); (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte *) buf); return bytes; } size_t test_read(int fd, void* chars, size_t count) { JNIEnv *env = NULL; if ((*javaVm)->AttachCurrentThread(javaVm, &env, NULL) != JNI_OK) { return -1; } if (NULL == fileHelperClz) { return -1; } jobject fileHelperObj = getFileHelper(); if (fileHelperObj == NULL) { return -1; } jmethodID readId = (*env)->GetMethodID(env, fileHelperClz, "read", "(I[BI)I"); jbyteArray byteArray = char2JByteArray(env, chars, (jint)count); jint len = (*env)->GetArrayLength(env, byteArray); jint result = (*env)->CallIntMethod(env, fileHelperObj, readId, fd, byteArray, (jint)count); jbyte* bytes = (*env)->GetByteArrayElements(env, byteArray, 0); memcpy(chars, bytes, len); (*env)->ReleaseByteArrayElements(env, byteArray, bytes, 0); return result; } size_t test_write(int fd, const void* in, size_t count) { JNIEnv *env = NULL; if ((*javaVm)->AttachCurrentThread(javaVm, &env, NULL) != JNI_OK) { return -1; } if (NULL == fileHelperClz) { return -1; } jobject fileHelperObj = getFileHelper(); if (fileHelperObj == NULL) { return -1; } jmethodID writeId = (*env)->GetMethodID(env, fileHelperClz, "write", "(I[BI)I"); jbyteArray byteArray = char2JByteArray(env, (char*)in, (jint)count); jint result = (*env)->CallIntMethod(env, fileHelperObj, writeId, fd, byteArray, (jint)count); return result; } int fclose(int fp) { JNIEnv *env = NULL; if ((*javaVm)->AttachCurrentThread(javaVm, &env, NULL) != JNI_OK) { return NULL; } if (NULL == fileHelperClz) { return -1; } jobject fileHelperObj = getFileHelper(); if (fileHelperObj == NULL) { return -1; } jmethodID closeId = (*env)->GetMethodID(env, fileHelperClz, "close", "(I)I"); jint result = (*env)->CallIntMethod(env, fileHelperObj, closeId, fp); (*env)->DeleteGlobalRef(env, fileHelperObj); (*env)->DeleteGlobalRef(env, fileHelperClz); return result; } JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { javaVm = vm; JNIEnv *env = NULL; jint result; if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) == JNI_OK) { result = JNI_VERSION_1_6; } else if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_4) == JNI_OK) { result = JNI_VERSION_1_4; } else { result = JNI_VERSION_1_2; } return result; }



