Android jni 常用接口示例

本文主要是介绍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));

你可能感兴趣的:(android编程知识,jni,android,java,jvm,c++)