本文主要是介绍jni 常用函数的实例
一、数组传递
1、Java传递数组到Jni层
Jni层接收到Java层传递过来的byte[]数组
1.1、GetByteArrayRegion
进行值拷贝,将Java端数组的数据拷贝到本地的数组中
void getDataFromJava(JNIEnv *env, jobject object, jbyteArray buffer, jint len){
unsigned char array[TEST_BUFFER_SIZE];
//直接将java端的数组拷贝到本地的数据中,建议使用这种方式,更加安全
//jint len=(*evn)->GetArrayLength(evn,array);//获取数组长度
//如果没有array 需要申请空间
//jbyte *array= (jbyte *)malloc(len * sizeof(jbyte));
env->GetByteArrayRegion(buffer, 0, len, array);
//可以通过array来访问这段数组的值
//注意:修改的只是本地的值,java端不会同时改变数组的值
int i = 0;
for (i = 0; i < TEST_BUFFER_SIZE; i++) {
array[i] = i;
}
}
1.2、GetByteArrayElements
指针的形式,将本地的数组指针直接指向Java端的数组地址,其实本质上是JVM在堆上分配的这个数组对象上增加一个引用计数,保证垃圾回收的时候不要释放,从而交给本地的指针使用,使用完毕后指针一定要记得通过ReleaseByteArrayElements进行释放,否则会产生内存泄露
void getDataFromJava(JNIEnv *env, jobject object, jbyteArray buffer, jint len) {
//将本地指针指向含有java端数组的内存地址,依赖jvm的具体实现
unsigned char *pBuffer = env->GetByteArrayElements(buffer, NULL);
if (pBuffer == NULL) {
return ;
}
int i = 0;
//可以通过pBuffer指针来访问这段数组的值
//注意:修改的是堆上的值,java端可能会同步改变,依赖于jvm的具体实现
//不建议通过本方法改变java端的数组值
for (i = 0; i < TEST_BUFFER_SIZE; i++) {
pBuffer[i] = i;
}
env->ReleaseByteArrayElements(buffer, pBuffer, 0);
GetPrimitiveArrayCritical
ReleasePrimitiveArrayCritical
获取java层的数组中的内容,方法为返回数组的拷贝,因此必须释放此资源
2、jni传递数组到java层
通过native函数的返回值来传递
jbyteArray getDataFromJni(JNIEnv *env, jobject object) {
//传递JNI层的数组数据到java端,通过返回值来传递
unsigned char buffer[TEST_BUFFER_SIZE];
int i = 0;
for (i = 0; i < TEST_BUFFER_SIZE;i ++) {
buffer[i] = i;
}
//分配ByteArray
jbyteArray array = env->NewByteArray(TEST_BUFFER_SIZE);
//将传递数据拷贝到java端
env->SetByteArrayRegion(array, 0, TEST_BUFFER_SIZE, buffer);
return array;
}
首先通过 NewByteArray 在堆上分配数组对象,然后通过SetByteArrayRegion 把本地的数组数据拷贝到堆上分配的数组中去,然后通过返回值将分配的数组对象返回到Java层即可。
二、字符串转换
1、jni–>java
jstring getHelloWorld(JNIEnv *env, jclass jc){
char Version[256] = {"Hello world!"};
jstring value = env->NewStringUTF((const char*)Version);
return value;
}
2、java–>jni
jstring helloworld( JNIEnv* env, jobject jobj,jstring jstr){
//utf8
const char* chr = env->GetStringUTFChars(jstr,0);
//unicode
const jchar* chr1 = env->GetStringChars(jstr,0);
jsize jlen = env->GetStringUTFLength(jstr);
jsize jlen1 = env->GetStringLength(jstr);
char c[120] = "你好 2017";
//把chr拼接到c
strcat(c,chr);
jstring new_str = env->NewStringUTF(c);
env->ReleaseStringUTFChars(jstr,chr);
env->ReleaseStringChars(jstr,chr1);
return new_str;
}
三、常用的接口使用示例
char * stringToChar(JNIEnv * env,jstring jstr){
char* rtn = NULL;
//等到String的class
jclass jcls = env->FindClass("java/lang/String");
//定义string的编码格式
jstring strcode = env->NewStringUTF("utf-8");
//获得string的getBytes method Id
jmethodID jmid = env->GetMethodID(jcls, "getBytes", "(Ljava/lang/String;)[B");
//调用getBytes方法 返回一个jbyteArray
jbyteArray byteArr= (jbyteArray)env->CallObjectMethod(jstr, jmid, strcode);
jsize alen = env->GetArrayLength(byteArr);
//数组指向一个byte指针
jbyte* pbyte = env->GetByteArrayElements(byteArr, 0);
if (alen > 0)
{
//申请数组长度+1个内存空间
rtn = (char*)malloc(alen + 1);
//从源pbyte所指的内存地址的起始位置开始拷贝alen个字节到目标rtn所指的内存地址的起始位置中
memcpy(rtn, pbyte, alen);
rtn[alen] = 0;
}
//释放
env->ReleaseByteArrayElements(byteArr, pbyte, 0);
return rtn;
}
JNIEnv* env, jobject fdObj;
int fd = jniGetFDFromFileDescriptor(env, fdObj);
jobject GetObjectArrayElement(jobjectArray array, jsize index)
说明;获取引用数据类型数组指定索引位置处的对象
参数:
array:引用数据类型数组
index:目标索引值
注:不需要释放
示例:
jobjectArray argArray;
jstring* argObjs = new jstring[numArgs];
for (int i = 0; i < numArgs; i++) {
argObjs[i] = (jstring)env->GetObjectArrayElement(argArray, i);
}
const char GetStringUTFChars(jstring string, jboolean isCopy)
说明:将 Java 风格的 jstring 对象转换成 C 风格的字符串,同上一个是 UTF-16 编码,一个是 UTF-8 编码
参数:
string:Java 风格的字符串
isCopy:是否进行拷贝操作,0 为不拷贝
void ReleaseStringUTFChars(jstring string, const char* utf)
说明:释放指定的字符串指针,通常来说,Get 和 Release 是成对出现的
参数:
string:Java 风格的字符串
chars/utf:对应的 C 风格的字符串
示例
const char** args = nullptr;
jstring* argObjs = new jstring[numArgs];
for (int i = 0; i < numArgs; i++) {
args[i] = env->GetStringUTFChars(argObjs[i], NULL);
}
env->ReleaseStringUTFChars(argObjs[i], args[i]);
NativeType GetArrayElements(ArrayType array, jboolean isCopy)
说明:获取基本数据类型数组的头指针
参数:
array:基本数据类型数组
isCopy:是否进行拷贝操作,0 为不拷贝
void ReleaseArrayElements(ArrayType array, NativeType* elems, jint mode)
说明:释放基本数据类型数组指针。通常来说,Get 和 Release 是成对出现的
参数:
array:基本数据类型数组
elems:对应的 C 风格的基本数据类型指针
mode:释放模式,通常我们都是使用 0,有三种,如下:
0:将内容复制回来并释放原生数组
JNI_COMMIT:将内容复制回来但不释放原生数组,一般用于周期性更新数组
JNI_ABORT:释放原生数组但不将内容复制回来
示例:
jbyteArray uuidObj;
jbyte* uuid = NULL;
uuid = env->GetByteArrayElements(uuidObj, NULL);
if (uuid)
env->ReleaseByteArrayElements(uuidObj, uuid, 0);
jclass FindClass(const char* name)
说明:根据类的全路径找到相应的 jclass 对象
参数:
name:类的全路径,例如 “Ljava/lang/String;”
jmethodID GetMethodID(jclass clazz, const char name, const char sig)
说明:获取类中某个非静态方法的ID
参数:
clazz:指定对象的类
name:这个方法在 Java 类中定义的名称,构造方法为 ““
sig:这个方法的类型描述符,例如 “()V”,其中括号内是方法的参数,括号后是返回值类型
NativeType CallMethod(jobject obj, jmethodID methodID, …)
obj:某个 Java 对象实例
methodID:指定方法的ID
args:输入参数列表
示例:
const char* className;
jclass myClass = env->FindClass(className);
jmethodID myMethod = env->GetMethodID(myClass, methodName, “()[B”);
(jbyteArray)env->CallObjectMethod(object, myMethod);
jobject NewGlobalRef(jobject obj)
说明:创建一个全局引用,不用时必须调用 DeleteGlobalRef() 方法释放。
参数:
obj:某个 Java 对象实例,可以是局部引用或全局引用
void DeleteGlobalRef(jobject globalRef)
说明:释放某个全局引用
参数:
globalRef:某全局引用
示例:
static jobject sJniCallbacksObj;
sJniCallbacksObj = env->NewGlobalRef(env->GetObjectField(obj, sJniCallbacksField));
env->DeleteGlobalRef(sJniCallbacksObj);
NativeType GetField(jobject obj, jfieldID fieldID)
说明:获取实例域的变量值
参数:
obj:某个 Java 对象实例
fieldID:这个变量的域ID
示例:
sJniCallbacksObj = env->NewGlobalRef(env->GetObjectField(obj, sJniCallbacksField));