本篇着重点是异常、字符串和数组的操作处理
一、异常
jint (*Throw)(JNIEnv*, jthrowable);
jint (*ThrowNew)(JNIEnv *, jclass, const char *);
jthrowable (*ExceptionOccurred)(JNIEnv*);
jboolean (*ExceptionCheck)(JNIEnv*);
void (*ExceptionDescribe)(JNIEnv*);
void (*ExceptionClear)(JNIEnv*);
void (*FatalError)(JNIEnv*, const char*);
1、检查异常
jthrowable (*ExceptionOccurred)(JNIEnv*);
jboolean (*ExceptionCheck)(JNIEnv*);
调用了JNI的ExceptionCheck函数检查最近一次JNI调用是否发生了异常,如果有异常这个函数返回JNI_TRUE,否则返回JNI_FALSE
异常检查JNI还提供了另外一个接口,ExceptionOccurred,如果检测有异常发生时,该函数会返回一个指向当前异常的引用。作用和ExceptionCheck一样,两者的区别在于返回值不一样。
2、抛出异常给上层
jint (*Throw)(JNIEnv*, jthrowable);
jint (*ThrowNew)(JNIEnv *, jclass, const char *);
Throw:丢弃一个现有的异常对象,在当前线程触发一个新的异常
ThrowNew:在当前线程触发一个异常,并自定义输出异常信息
3、打印异常堆栈信息
void (*ExceptionDescribe)(JNIEnv*);
4、清除异常
void (*ExceptionClear)(JNIEnv*);
示例
JavaClass.java
package com.anniljing.jnidemo;
import android.util.Log;
public class JavaClass {
public static String name;
private String version = "hello";
private static int sCode;
public JavaClass() {
Log.d("MainActivity", "Init JavaClass");
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public static void setCode(int code) {
sCode = code;
}
@Override
public String toString() {
return "JavaClass{" +
"version='" + version + '\'' + "Code=" + sCode + '\'' +
'}';
}
public void getResulte() {
int data[]=new int[2];
data[3]=5;
Log.d("JavaClass", "getResulte");
}
}
HandleException.java
package com.anniljing.jnidemo.ExceptionHandle;
public class HandleException {
public static native void handleJniExeption();
}
HandleException.cpp
#include <jni.h>
#include <AndroidLog.h>
#include <jni.h>
#include <jni.h>
extern "C"
JNIEXPORT void JNICALL
Java_com_anniljing_jnidemo_ExceptionHandle_HandleException_handleJniExeption(JNIEnv *env,
jclass clazz) {
//向 Java 层抛出异常
jclass exceptionClz = env->FindClass("com/anniljing/jnidemo/JavaClass");
jmethodID constructure = env->GetMethodID(exceptionClz, "<init>", "()V");
jobject obj = env->NewObject(exceptionClz, constructure);
jmethodID getResulteID = env->GetMethodID(exceptionClz, "getResulte", "()V");
env->CallVoidMethod(obj, getResulteID);
//检查当前环境是否发生异常(类似于 Java try{})
if (env->ExceptionCheck()) {
// 处理异常(类似于 Java 的 catch{})
env->ExceptionDescribe();
LOGD("exception from native");
}
//清除异常
env->ExceptionClear();
}
二、字符串操作
jstring (*NewString)(JNIEnv*, const jchar*, jsize);
jsize (*GetStringLength)(JNIEnv*, jstring);
const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*);
void (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*);
jstring (*NewStringUTF)(JNIEnv*, const char*);
jsize (*GetStringUTFLength)(JNIEnv*, jstring);
const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
void (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*);
1、构造新的java.lang.String对象
jstring (*NewString)(JNIEnv*, const jchar*, jsize);
jstring (*NewStringUTF)(JNIEnv*, const char*);
2、获取字符串的长度
jsize (*GetStringLength)(JNIEnv*, jstring);
jsize (*GetStringUTFLength)(JNIEnv*, jstring);
3、获取字符串的数组指针
const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*);
const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
4、通知 VM 本机代码不再需要访问
void (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*);
void (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*);
示例
StringHandle.java
package com.anniljing.jnidemo.StringHandle;
public class StringHandle {
public static native String stringUTFHandle();
}
StringHandle.cpp
#include <jni.h>
#include <AndroidLog.h>
#include <cstdlib>
extern "C"
JNIEXPORT jstring JNICALL
Java_com_anniljing_jnidemo_StringHandle_StringHandle_stringUTFHandle(JNIEnv *env, jclass clazz) {
jstring newNativeString = env->NewStringUTF("Hello string from native");
//定义一个const char数组
const char *_str = NULL;
jsize stringSize = env->GetStringUTFLength(newNativeString);
LOGD("String size is:%d", stringSize);
jboolean isCopy = false;
//将java字符串转为UTF-8格式的const char数组
_str = env->GetStringUTFChars(newNativeString, &isCopy);
LOGD("GetStringUTFChars is:%s", _str);
env->ReleaseStringUTFChars(newNativeString, _str);
//在栈上分配buffer,不需要手动释放,函数结束自动释放
char inputbuf[128];
env->GetStringUTFRegion(newNativeString, 0, 6, inputbuf);
LOGD("GetStringUTFRegion is:%s", inputbuf);
//在自由存储区分配buffer空间。自由存储区不仅可以是堆,还可以是静态存储区
char *p = new char[128];
env->GetStringUTFRegion(newNativeString, 0, 6, p);
LOGD("GetStringUTFRegion with new:%s", p);
//释放自由存储区空间,与new[]关键字成对使用
delete[] p;
//在堆分配buffer空间
char *ptr = (char *) malloc(sizeof(char) * 6);
env->GetStringUTFRegion(newNativeString, 0, 6, ptr);
LOGD("GetStringUTFRegion with malloc:%s", ptr);
//释放堆空间,与malloc函数成对使用
free(ptr);
//释放const char数组
env->ReleaseStringUTFChars(newNativeString, _str);
return newNativeString;
}
三、数组相关的操作
1、基本数据类型的数组操作
1.1、创建新的数组
jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize);
jbyteArray (*NewByteArray)(JNIEnv*, jsize);
jcharArray (*NewCharArray)(JNIEnv*, jsize);
jshortArray (*NewShortArray)(JNIEnv*, jsize);
jintArray (*NewIntArray)(JNIEnv*, jsize);
jlongArray (*NewLongArray)(JNIEnv*, jsize);
jfloatArray (*NewFloatArray)(JNIEnv*, jsize);
jdoubleArray (*NewDoubleArray)(JNIEnv*, jsize);
1.2、获取数组的元素
jboolean* (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*);
jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
jchar* (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*);
jshort* (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*);
jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
jlong* (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*);
jfloat* (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*);
jdouble* (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*);
1.3、获取指定范围的数组
void (*GetBooleanArrayRegion)(JNIEnv*, jbooleanArray,jsize, jsize, jboolean*);
void (*GetByteArrayRegion)(JNIEnv*, jbyteArray,jsize, jsize, jbyte*);
void (*GetCharArrayRegion)(JNIEnv*, jcharArray,jsize, jsize, jchar*);
void (*GetShortArrayRegion)(JNIEnv*, jshortArray,jsize, jsize, jshort*);
void (*GetIntArrayRegion)(JNIEnv*, jintArray,jsize, jsize, jint*);
void (*GetLongArrayRegion)(JNIEnv*, jlongArray,jsize, jsize, jlong*);
void (*GetFloatArrayRegion)(JNIEnv*, jfloatArray,jsize, jsize, jfloat*);
void (*GetDoubleArrayRegion)(JNIEnv*, jdoubleArray,jsize, jsize, jdouble*);
1.4、设置指定范围的数组
void (*SetBooleanArrayRegion)(JNIEnv*, jbooleanArray,jsize, jsize, const jboolean*);
void (*SetByteArrayRegion)(JNIEnv*, jbyteArray,jsize, jsize, const jbyte*);
void (*SetCharArrayRegion)(JNIEnv*, jcharArray,jsize, jsize, const jchar*);
void (*SetShortArrayRegion)(JNIEnv*, jshortArray,jsize, jsize, const jshort*);
void (*SetIntArrayRegion)(JNIEnv*, jintArray,jsize, jsize, const jint*);
void (*SetLongArrayRegion)(JNIEnv*, jlongArray,jsize, jsize, const jlong*);
void (*SetFloatArrayRegion)(JNIEnv*, jfloatArray,jsize, jsize, const jfloat*);
void (*SetDoubleArrayRegion)(JNIEnv*, jdoubleArray,jsize, jsize, const jdouble*);
1.5、native层释放数组资源
void (*ReleaseBooleanArrayElements)(JNIEnv*, jbooleanArray,jboolean*, jint);
void (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray,jbyte*, jint);
void (*ReleaseCharArrayElements)(JNIEnv*, jcharArray,jchar*, jint);
void (*ReleaseShortArrayElements)(JNIEnv*, jshortArray,jshort*, jint);
void (*ReleaseIntArrayElements)(JNIEnv*, jintArray,jint*, jint);
void (*ReleaseLongArrayElements)(JNIEnv*, jlongArray,jlong*, jint);
void (*ReleaseFloatArrayElements)(JNIEnv*, jfloatArray,jfloat*, jint);
void (*ReleaseDoubleArrayElements)(JNIEnv*, jdoubleArray,jdouble*, jint);
示例
ArrayHandle.java
package com.anniljing.jnidemo.ArrayHandle;
public class ArrayHandle {
//从native层返回int类型的数组
public static native int[] getArrayData();
//java层传入native层,native处理以后,返回新的数组
public static native int[] handleJavaArray(int[] srcData);
}
ArrayHandle.cpp
#include <jni.h>
#include <AndroidLog.h>
#include <string.h>
extern "C"
JNIEXPORT jintArray JNICALL
Java_com_anniljing_jnidemo_ArrayHandle_ArrayHandle_getArrayData(JNIEnv *env, jclass clazz) {
jintArray intArray = env->NewIntArray(10);
//通过GetIntArrayElements拿到C类型的数组的指针,然后才能进行C数组的操作
jint *element = env->GetIntArrayElements(intArray, NULL);
LOGD("Before GetIntArrayElements element[0]:%d", *element);
jint len = env->GetArrayLength(intArray);
for (int i = 0; i < len; ++i) {
*(element + i) = (i * 10 + 1);
LOGD("For intArray:address(%p),value(%d)", (element + i), *(element + i));
LOGD("elment address:%p", element);
}
env->ReleaseIntArrayElements(intArray, element, 0);
return intArray;
}
extern "C"
JNIEXPORT jintArray JNICALL
Java_com_anniljing_jnidemo_ArrayHandle_ArrayHandle_handleJavaArray(JNIEnv *env, jclass clazz,
jintArray java_data) {
jintArray result = env->NewIntArray(2);
jint buf[2];
//将java_data数据复制到buf里面
env->GetIntArrayRegion(java_data, 0, 2, buf);
for (int i = 0; i < 2; ++i) {
LOGD("buf data:%d", buf[i]);
}
//将buf数据复制到result里面
env->SetIntArrayRegion(result, 0, 2, buf);
return result;
}
MainActivity.java
public void handleArray(View view) {
int[] arrayData = ArrayHandle.getArrayData();
for (int i = 0; i < arrayData.length; i++) {
Log.d(TAG, "Native生成的数据:" + arrayData[i]);
}
int[] javaData = {1, 3, 5, 7, 9};
int[] destData = ArrayHandle.handleJavaArray(javaData);
for (int i = 0; i < destData.length; i++) {
Log.d(TAG, "Native转换后的数据:" + destData[i]);
}
}
2、String以及object类型数组的操作
2.1、获取数组下标元素
jobject (*GetObjectArrayElement)(JNIEnv*, jobjectArray, jsize);
2.2、设置指定下标元素
void (*SetObjectArrayElement)(JNIEnv*, jobjectArray, jsize, jobject);
2.3、获取数组长度
jsize (*GetArrayLength)(JNIEnv*, jarray);
示例
JavaClass.java
package com.anniljing.jnidemo;
import android.util.Log;
public class JavaClass {
public static String name;
private String version = "hello";
private static int sCode;
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public static void setCode(int code) {
sCode = code;
}
@Override
public String toString() {
return "JavaClass{" +
"version='" + version + '\'' + "Code=" + sCode + '\'' +
'}';
}
public void getResulte() {
int data[]=new int[2];
data[3]=5;
Log.d("JavaClass", "getResulte");
}
}
ArrayHandle.java
package com.anniljing.jnidemo.ArrayHandle;
public class ArrayHandle {
//处理String类型的数组
public static native String[] handleStringArray(String[] srcData);
//处理Object类型的数组
public static native Object[] handleObjectArray(Object[] objects);
}
ArrayHandle.cpp
#include <jni.h>
#include <AndroidLog.h>
#include <string.h>
#include <stdlib.h>
extern "C"
JNIEXPORT jobjectArray JNICALL
Java_com_anniljing_jnidemo_ArrayHandle_ArrayHandle_handleStringArray(JNIEnv *env, jclass clazz,
jobjectArray src_data) {
jint len = env->GetArrayLength(src_data);
jboolean isCopy = false;
for (int i = 0; i < len; ++i) {
const char *content = NULL;
jstring element = static_cast<jstring>(env->GetObjectArrayElement(src_data, i));
content = env->GetStringUTFChars(element, &isCopy);
const size_t contentLen = strlen(content);
char *replace = new char[contentLen + 1];
strncpy(replace, content, contentLen);
memcpy(replace, "李", strlen("李"));
jstring last = env->NewStringUTF(replace);
env->SetObjectArrayElement(src_data, i, last);
LOGD("Replace content:%s", replace);
delete[] replace;
}
return src_data;
}
extern "C"
JNIEXPORT jobjectArray JNICALL
Java_com_anniljing_jnidemo_ArrayHandle_ArrayHandle_handleObjectArray(JNIEnv *env, jclass clazz,
jobjectArray objects) {
jclass javaClass = env->FindClass("com/anniljing/jnidemo/JavaClass");
int len = env->GetArrayLength(objects);
for (int i = 0; i < len; ++i) {
jobject javaJObject = env->GetObjectArrayElement(objects, i);
jfieldID versionField = env->GetFieldID(javaClass, "version", "Ljava/lang/String;");
jstring versionValue = static_cast<jstring>(env->GetObjectField(javaJObject, versionField));
const char *_version = NULL;
_version = env->GetStringUTFChars(versionValue, NULL);
LOGD("%s", _version);
char *nativeVersion = new char[30];
strcpy(nativeVersion, "Native version v1.0.");
char *index = new char[10];
sprintf(index, "%d", i);
strcat(nativeVersion, index);
jmethodID setVersion = env->GetMethodID(javaClass, "setVersion", "(Ljava/lang/String;)V");
env->CallVoidMethod(javaJObject, setVersion, env->NewStringUTF(nativeVersion));
env->SetObjectArrayElement(objects, i, javaJObject);
}
return objects;
}
MainActivity.java
public void handleArray(View view) {
String[] names = new String[4];
names[0] = "张三";
names[1] = "张四";
names[2] = "张五";
names[3] = "张六";
try {
ArrayHandle.handleStringArray(names);
} catch (Exception e) {
e.printStackTrace();
}
for (int i = 0; i < names.length; i++) {
Log.d(TAG, names[i]);
}
JavaClass[] javaClasses = new JavaClass[10];
int len = javaClasses.length;
for (int i = 0; i < len; i++) {
JavaClass javaClass = new JavaClass();
javaClass.setVersion("java version:1.0." + i);
javaClasses[i] = javaClass;
}
JavaClass[] objects = (JavaClass[]) ArrayHandle.handleObjectArray(javaClasses);
for (int i = 0; i < objects.length; i++) {
Log.d(TAG, objects[i].getVersion());
}
}
源码已同步gitHub:https://github.com/AinialJing/JniDemo