C风格:(*env)->NewStringUTF(env, "Hellow World!");
C++风格:env->NewStringUTF("Hellow World!");
这里我们使用 C++ 风格作为示例。
1. jclass FindClass(const char* name)
2. jclass GetSuperclass(jclass clazz)
3. jboolean IsAssignableFrom(jclass clazz1, jclass clazz2)
1. jobject AllocObject(jclass clazz)
2. jobject NewObject(jclass clazz, jmethodID methodID, …)
3. jobject NewObjectA(jclass clazz, jmethodID methodID, jvalue* args)
4. jobject NewObjectV(jclass clazz, jmethodID methodID, va_list args)
5. jclass GetObjectClass(jobject obj)
6. jobjectRefType GetObjectRefType(jobject obj)
7. jboolean IsInstanceOf(jobject obj, jclass clazz)
8. jboolean IsSameObject(jobject ref1, jobject ref2)
1. jfieldID GetFieldID(jclass clazz, const char name, const char sig)
2. NativeType GetField(jobject obj, jfieldID fieldID)
3. void SetField(jobject obj, jfieldID fieldID, NativeType value)
4. jfieldID GetStaticFieldID(jclass clazz, const char name, const char sig)
5. NativeType GetStaticField(jobject obj, jfieldID fieldID);
6. void SetStaticField(jobject obj, jfieldID fieldID, NativeType value)
1. jmethodID GetMethodID(jclass clazz, const char name, const char sig)
2. NativeType CallMethod(jobject obj, jmethodID methodID, …)
3. NativeType CallMethodA(jobject obj, jmethodID methodID, jvalue* args)
4. NativeType CallMethodV(jobject obj, jmethodID methodID, va_list args)
5. jmethodID GetStaticMethodID(jclass clazz, const char name, const char sig)
6. NativeType CallStaticMethod(jclass clazz, jmethodID methodID, …)
7. NativeType CallStaticMethodA(jclass clazz, jmethodID methodID, jvalue* args)
8. NativeType CallStaticMethodV(jclass clazz, jmethodID methodID, va_list args)
1. jobject NewGlobalRef(jobject obj)
2. void DeleteGlobalRef(jobject globalRef)
3. jobject NewLocalRef(jobject ref)
4. void DeleteLocalRef(jobject localRef)
5. jweak NewWeakGlobalRef(jobject obj)
6. void DeleteWeakGlobalRef(jweak obj)
1. jstring NewString(const jchar* unicodeChars, jsize len)
2. jstring NewStringUTF(const char* bytes)
3. jsize GetStringLength(jstring string)
4. jsize GetStringUTFLength(jstring string)
5. const jchar GetStringChars(jstring string, jboolean isCopy)
6. const char GetStringUTFChars(jstring string, jboolean isCopy)
7. void ReleaseStringChars(jstring string, const jchar* chars)
8. void ReleaseStringUTFChars(jstring string, const char* utf)
1. jobjectArray NewObjectArray(jsize length, jclass elementClass, jobject initialElement)
2.ArrayType NewArray(jsize length)
3. jsize GetArrayLength(jarray array)
4. jobject GetObjectArrayElement(jobjectArray array, jsize index)
5. void SetObjectArrayElement(jobjectArray array, jsize index, jobject value)
6. NativeType GetArrayElements(ArrayType array, jboolean isCopy)
7. void ReleaseArrayElements(ArrayType array, NativeType* elems, jint mode)
8. void GetArrayRegion(ArrayType array, jsize start, jsize len, NativeType* buf)
9. void SetArrayRegion(ArrayType array, jsize start, jsize len, const NativeType* buf)
2. jint ThrowNew(jclass clazz, const char* message)
3. jthrowable ExceptionOccurred()
7. void FatalError(const char* msg)
说明:获取当前 JNI 的版本号
返回值:
#define JNI_VERSION_1_1 0x00010001
#define JNI_VERSION_1_2 0x00010002
#define JNI_VERSION_1_4 0x00010004
#define JNI_VERSION_1_6 0x00010006
说明:根据类的全路径找到相应的 jclass 对象
参数:
示例:
jclass mStringClass = env->FindClass("Ljava/lang/String;");
说明:返回一个类的父类,如果 clazz 是 Object 类,没有父类,那么将返回 NULL
参数:
示例:
jclass clazz = env->GetSuperclass(mStringClass); // clazz is Ljava/lang/Object;
说明:判断类1是否可以安全的强制转换类2
参数:
说明:不调用构造方法创建实例
参数:
说明:使用指定的构造方法创建类的实例,唯一不同的是输入参数的传入形式不同
参数:
示例:
jclass rect_clazz = env->FindClass("android/graphics/Rect");
jmethodID rect_constructor = env->GetMethodID(rect_clazz, "<init>", "()V");
jobject rect = env->NewObject(rect_clazz, rect_constructor);
说明:根据对象获取所属类
参数:
说明:获取到对象的引用类型,JNI 1.6 新增的方法
参数:
返回:
JNIInvalidRefType = 0 // 该 obj 是个无效的引用
JNILocalRefType = 1 // 该 obj 是个局部引用
JNIGlobalRefType = 2 // 该 obj 是个全局引用
JNIWeakGlobalRefType = 3 // 该 obj 是个全局的弱引用
说明:判断某个对象是否是指定类的实例
参数:
说明:判断两个对象的引用是否指向的是相同的 Java 对象
参数:
说明:获取类中某个非静态成员变量的ID(域ID)
参数:
示例:
jclass clazz = env->FindClass("android/graphics/Rect");
jfieldID left_field = env->GetFieldID(clazz, "left", "I");
jfieldID top_field = env->GetFieldID(clazz, "top", "I");
jfieldID right_field = env->GetFieldID(clazz, "right", "I");
jfieldID bottom_field = env->GetFieldID(clazz, "bottom", "I");
说明:获取实例域的变量值,这里 type 表示的是一系列方法,如下:
Get<type>Field Routine Name | Native Type |
GetObjectField() | jobject |
GetBooleanField() | jboolean |
GetByteField() | jbyte |
GetCharField() | jchar |
GetShortField() | jshort |
GetIntField() | jint |
GetLongField() | jlong |
GetFloatField() | jfloat |
GetDoubleField() | jdouble |
参数:
示例:
jobject rect; // 初始化过程省略
jclass clazz = env->FindClass("android/graphics/Rect");
jfieldID left_field = env->GetFieldID(clazz, "left", "I");
jint left = env->GetIntField(rect, left_field);
说明:修改实例域的变量值,这里 type 对应上面的 Get 方法,不再累述
参数:
示例:
jobject rect; // 初始化过程省略
jclass clazz = env->FindClass("android/graphics/Rect");
jfieldID left_field = env->GetFieldID(clazz, "left", "I");
env->SetIntField(rect, left_field, 1);
说明:同 GetFieldID,只不过这里操作的是静态的域(Filed)
说明:同 GetField,只不过这里操作的是静态的域(Filed)
说明:同 SetField,只不过这里操作的是静态的域(Filed)
说明:获取类中某个非静态方法的ID
参数:
示例:
Java 的类定义如下:
package com.afei.jnidemo;
class Test {
public Test(){}
public int show(String msg, int number) {
System.out.println("msg: " + msg);
System.out.println("number: " + number);
return 0;
}
}
JNI 调用如下:
jclass clazz = env->FindClass("com/afei/jnidemo/Test");
jmethodID constructor_method = env->GetMethodID(clazz, "<init>", "()V");
jmethodID show_method = env->GetMethodID(clazz, "show", "(Ljava/lang/String;I)I");
签名时其中括号内是方法的参数,括号后是返回值类型。例如 show 方法,第一个参数是 String 类,对应 Ljava/lang/String;(注意后面有一个分号),第二个参数是 int 基本类型,对应的类型描述符是 I,返回值也是 int,同样是 I,所以最终该方法的签名为 “(Ljava/lang/String;I)I”。
说明:调用对象的某个方法,唯一不同的是输入参数的传入形式不同,这里 type 表示的是一系列方法,如下:
Call<type>Method Routine Name | Native Type |
CallVoidMethod() CallVoidMethodA() CallVoidMethodV() | void |
CallObjectMethod() CallObjectMethodA() CallObjectMethodV() | jobject |
CallBooleanMethod() CallBooleanMethodA() CallBooleanMethodV() | jboolean |
CallByteMethod() CallByteMethodA() CallByteMethodV() | jbyte |
CallCharMethod() CallCharMethodA() CallCharMethodV() | jchar |
CallShortMethod() CallShortMethodA() CallShortMethodV() | jshort |
CallIntMethod() CallIntMethodA() CallIntMethodV() | jint |
CallLongMethod() CallLongMethodA() CallLongMethodV() | jlong |
CallFloatMethod() CallFloatMethodA() CallFloatMethodV() | jfloat |
CallDoubleMethod() CallDoubleMethodA() CallDoubleMethodV() | jdouble |
参数:
示例:
jclass clazz = env->FindClass("com/afei/jnidemo/Test");
jmethodID show_method = env->GetMethodID(clazz, "show", "(Ljava/lang/String;I)I");
jint result = env->CallIntMethod(clazz, show_method, "Hello JNI!", 0);
说明:同 GetMethodID,只不过操作的是静态方法
说明:同 NativeType CallMethod,只不过操作的是静态方法,参数也由 jobject 变成了 jclass。
说明:创建一个全局引用,不用时必须调用 DeleteGlobalRef() 方法释放。
参数:
示例:
jclass mPointFClass; // global reference to PointF class
...
jclass clazz = env->FindClass("android/graphics/PointF");
mPointFClass = (jclass) env->NewGlobalRef(clazz);
说明:释放某个全局引用
参数:
说明:创建一个局部引用。这个方法一般很少用。
参数:
说明:释放某个局部引用
参数:
注意:
局部引用在方法执行完后也会自动释放,不过当你在执行一个很大的循环时,里面会产生大量临时的局部引用,那么建议的做法是手动的调用该方法去释放这个局部引用。
说明:创建一个全局的弱引用
参数:
注意:
弱引用不会阻止 GC 回收它引用的对象,在内存不足时,弱引用的对象往往会被回收掉,使用时一定要多加小心。
说明:释放某个全局的弱引用
参数:
说明:以 UTF-16 的编码方式创建一个 Java 的字符串(jchar 的定义为 uint16_t)
参数:
说明:以 UTF-8 的编码方式创建一个 Java 的字符串
参数:
说明:获取字符串的长度,GetStringLength 是 UTF-16 编码,GetStringUTFLength 是 UTF-8 编码
参数:
说明:将 Java 风格的 jstring 对象转换成 C 风格的字符串,同上一个是 UTF-16 编码,一个是 UTF-8 编码
参数:
说明:释放指定的字符串指针,通常来说,Get 和 Release 是成对出现的
参数:
示例:
JNIEXPORT void JNICALL
Java_com_afei_jnidemo_MainActivity_test(JNIEnv *env,
jobject instance, jstring msg_) {
const char *msg = env->GetStringUTFChars(msg_, 0);
// Do Something
env->ReleaseStringUTFChars(msg_, msg);
}
说明:创建引用数据类型的数组
参数:
示例:
int points_count = 21;
jclass pointFClass = env->FindClass("android/graphics/PointF");
jobjectArray point_array = env->NewObjectArray(
points_count, pointFClass, NULL);
说明:创建基本数据类型的数组。这里的基本数据类型有:
New<PrimitiveType>Array Routines | Array Type |
NewBooleanArray() | jbooleanArray |
NewByteArray() | jbyteArray |
NewCharArray() | jcharArray |
NewShortArray() | jshortArray |
NewIntArray() | jintArray |
NewLongArray() | jlongArray |
NewFloatArray() | jfloatArray |
NewDoubleArray() | jdoubleArray |
参数:
说明:获取数组的长度
参数:
说明:获取引用数据类型数组指定索引位置处的对象
参数:
说明:设置引用数据类型数组指定索引位置处的值
参数:
说明:获取基本数据类型数组的头指针
参数:
说明:释放基本数据类型数组指针。通常来说,Get 和 Release 是成对出现的
参数:
0 | 将内容复制回来并释放原生数组 |
JNI_COMMIT | 将内容复制回来但不释放原生数组,一般用于周期性更新数组 |
JNI_ABORT | 释放原生数组但不将内容复制回来 |
示例:
Java_com_afei_jnidemo_MainActivity_test(JNIEnv *env,
jobject instance, jintArray array_) {
jint *array = env->GetIntArrayElements(array_, 0);
// Do Something
env->ReleaseIntArrayElements(array_, array, 0);
}
说明:返回基本数据类型数组的部分副本。这里的基本数据类型有:
Get<PrimitiveType>ArrayRegion Routine | Array Type | Native Type |
GetBooleanArrayRegion() | jbooleanArray | jboolean |
GetByteArrayRegion() | jbyteArray | jbyte |
GetCharArrayRegion() | jcharArray | jchar |
GetShortArrayRegion() | jshortArray | jhort |
GetIntArrayRegion() | jintArray | jint |
GetLongArrayRegion() | jlongArray | jlong |
GetFloatArrayRegion() | jfloatArray | jloat |
GetDoubleArrayRegion() | jdoubleArray | jdouble |
参数:
说明:设置基本数据类型数组元素。类型和上面的表类似。
参数:
说明:抛出一个异常,需要手动创建异常的实例,调用较复杂,一般不使用这个方法
参数:
示例:
jclass ioExceptionClazz = env->FindClass("java/io/IOException");
jmethodID ioExceptionConstructor = env->GetMethodID(ioExceptionClazz, "<init>", "(Ljava/lang/String;)V");
jthrowable exceptionObj = static_cast<jthrowable>(env->NewObject(ioExceptionClazz,
ioExceptionConstructor, "IO异常"));
if (env->Throw(exceptionObj) == JNI_OK) {
// 创建成功
} else {
// 创建失败
}
说明:抛出一个异常。使用起来比三个方法方便
参数:
示例:
if (env->ThrowNew(env->FindClass("java/io/IOException"), "IO异常") == JNI_OK) {
// 创建成功
} else {
// 创建失败
}
说明:检查是否有异常,如果本地函数有异常抛出,ExceptionOccurred 会返回这个异常的示例,ExceptionCheck 只返回是否有异常
说明:将异常和堆栈信息推送到错误流
说明:清除掉发生的异常
以上几个方法的示例:
Java 部分代码为:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
@Override
protected void
onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
test();
} catch (Exception e) {
Log.e("MainActivity", "onCreate: " + e);
}
}
private native void test() throws IllegalArgumentException;
private void callNullPointerException() throws NullPointerException {
throw new NullPointerException("MainActivity NullPointerException");
}
}
JNI 部分代码为:
JNIEXPORT void JNICALL
Java_com_afei_jnidemo_MainActivity_test(JNIEnv *env, jobject instance) {
jclass clazz = env->GetObjectClass(instance);
jmethodID mid =env->GetMethodID(clazz, "callNullPointerException", "()V");
env->CallVoidMethod(instance, mid); // will throw a NullPointerException
jthrowable exc = env->ExceptionOccurred(); // 检测是否发生异常
if (exc) {
LOGD("============");
env->ExceptionDescribe(); // 打印异常信息
LOGD("============");
env->ExceptionClear(); // 清除掉发生的异常
jclass newExcCls = env->FindClass("java/lang/IllegalArgumentException");
env->ThrowNew(newExcCls, "throw from JNI"); // 返回一个新的异常到 Java
}
}
运行结果为:
07-12 14:57:07.443 26623-26623/com.afei.jnidemo D/FaceAPI: ============
07-12 14:57:07.443 26623-26623/com.afei.jnidemo W/System.err: java.lang.NullPointerException: MainActivity NullPointerException
at com.afei.jnidemo.MainActivity.callNullPointerException(MainActivity.java:34)
at com.afei.jnidemo.MainActivity.test(Native Method)
at com.afei.jnidemo.MainActivity.onCreate(MainActivity.java:25)
at android.app.Activity.performCreate(Activity.java:6857)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1125)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2702)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2810)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1532)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:181)
at android.app.ActivityThread.main(ActivityThread.java:6288)
at java.lang.reflect.Method.invoke(Native Method)
07-12 14:57:07.443 26623-26623/com.afei.jnidemo W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:900)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:790)
07-12 14:57:07.443 26623-26623/com.afei.jnidemo D/FaceAPI: ============
07-12 14:57:07.444 26623-26623/com.afei.jnidemo E/MainActivity: onCreate: java.lang.IllegalArgumentException: throw from JNI
说明:抛出一个致命异常,并且不希望JVM处理
参数:
其他还有有关 Monitor Operations、NIO Support、Reflection Support 等一些方法,由于我也没使用过,就不再这里解释了。
可以参考 JNI 的官网的介绍:https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp9502