一、说明
这里介绍的函数大多是 NDK 开发中常用的函数,但并不是全部,内容稍多,基本可以满足我们的开发需求了,建议通过目录索引来找需要了解的。
这里的函数都是 JNIEnv 操作的相关函数,JNI_OnLoad 等 JavaVM 的方法不在这里介绍。
JNI 有 C、C++ 两种代码风格,即:
1 | C风格:(*env)->NewStringUTF(env, "Hellow World!"); |
这里我们使用 C++ 风格作为示例。
二、获取版本
- jint GetVersion()
说明:获取当前 JNI 的版本号
返回值:
1 | #define JNI_VERSION_1_1 0x00010001 |
三、类操作
jclass FindClass(const char* name)
说明:根据类的全路径找到相应的 jclass 对象
参数:
- name:类的全路径,例如 “Ljava/lang/String;”
示例:
1 | jclass mStringClass = env->FindClass("Ljava/lang/String;"); |
jclass GetSuperclass(jclass clazz)
说明:返回一个类的父类,如果 clazz 是 Object 类,没有父类,那么将返回 NULL
参数:
- jclazz:当前类对象
示例:
1 | jclass clazz = env->GetSuperclass(mStringClass); // clazz is Ljava/lang/Object; |
jboolean IsAssignableFrom(jclass clazz1, jclass clazz2)
说明:判断类1是否可以安全的强制转换类2
参数:
- clazz1:类1
- clazz2:类2
四、对象操作
jobject AllocObject(jclass clazz)
说明:不调用构造方法创建实例
参数:
- clazz:指定对象的类
jobject NewObject(jclass clazz, jmethodID methodID, …)
jobject NewObjectA(jclass clazz, jmethodID methodID, jvalue* args)
jobject NewObjectV(jclass clazz, jmethodID methodID, va_list args)
说明:使用指定的构造方法创建类的实例,唯一不同的是输入参数的传入形式不同
参数:
clazz:指定对象的类
methodID:指定的构造方法
args:输入参数列表
示例:
1
2
3jclass rect_clazz = env->FindClass("android/graphics/Rect");
jmethodID rect_constructor = env->GetMethodID(rect_clazz, "<init>", "()V");
jobject rect = env->NewObject(rect_clazz, rect_constructor);
jclass GetObjectClass(jobject obj)
说明:根据对象获取所属类
参数:
- obj:某个 Java 对象实例,不能为 NULL
jobjectRefType GetObjectRefType(jobject obj)
说明:获取到对象的引用类型,JNI 1.6 新增的方法
参数:
obj:某个 Java 对象实例
返回:JNIInvalidRefType = 0 // 该 obj 是个无效的引用
JNILocalRefType = 1 // 该 obj 是个局部引用
JNIGlobalRefType = 2 // 该 obj 是个全局引用
JNIWeakGlobalRefType = 3 // 该 obj 是个全局的弱引用
jboolean IsInstanceOf(jobject obj, jclass clazz)
说明:判断某个对象是否是指定类的实例
参数:
- obj:某个 Java 对象实例
- clazz:指定的类对象
jboolean IsSameObject(jobject ref1, jobject ref2)
说明:判断两个对象的引用是否指向的是相同的 Java 对象
参数:
- ref1:某个 Java 对象的引用1
- ref2:某个 Java 对象的引用2
五、域操作
jfieldID GetFieldID(jclass clazz, const char name, const char sig)
说明:获取类中某个非静态成员变量的ID(域ID)
参数:
clazz:指定对象的类
name:这个域(Field)在 Java 类中定义的名字
sig:这个域(Field)的类型描述符
示例:1
2
3
4
5jclass 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");
NativeType Get<type>Field(jobject obj, jfieldID fieldID)
说明:获取实例域的变量值,这里 type 表示的是一系列方法,如下:
Get |
Native Type |
---|---|
GetObjectField() | jobject |
GetBooleanField() | jboolean |
GetByteField() | jbyte |
GetCharField() | jchar |
GetShortField() | jshort |
GetIntField() | jint |
GetLongField() | jlong |
GetFloatField() | jfloat |
GetDoubleField() | jdouble |
参数:
obj:某个 Java 对象实例
fieldID:这个变量的域ID
示例:1
2
3
4jobject rect; // 初始化过程省略
jclass clazz = env->FindClass("android/graphics/Rect");
jfieldID left_field = env->GetFieldID(clazz, "left", "I");
jint left = env->GetIntField(rect, left_field);
void Set<type>Field(jobject obj, jfieldID fieldID, NativeType value)
说明:修改实例域的变量值,这里 type 对应上面的 Get 方法,不再累述
参数:
obj:需要修改的 Java 对象实例
fieldID:这个变量的域ID
value:需要设置的值
示例:1
2
3
4jobject rect; // 初始化过程省略
jclass clazz = env->FindClass("android/graphics/Rect");
jfieldID left_field = env->GetFieldID(clazz, "left", "I");
env->SetIntField(rect, left_field, 1);
jfieldID GetStaticFieldID(jclass clazz, const char name, const char sig)
说明:同 GetFieldID,只不过这里操作的是静态的域(Filed)NativeType GetStaticField(jobject obj, jfieldID fieldID)
说明:同 GetField,只不过这里操作的是静态的域(Filed)void SetStaticField(jobject obj, jfieldID fieldID, NativeType value)
说明:同 SetField,只不过这里操作的是静态的域(Filed)
六、方法操作
jmethodID GetMethodID(jclass clazz, const char name, const char sig)
说明:获取类中某个非静态方法的ID
参数:
- clazz:指定对象的类
- name:这个方法在 Java 类中定义的名称,构造方法为 ““
- sig:这个方法的类型描述符,例如 “()V”,其中括号内是方法的参数,括号后是返回值类型
示例:
Java 的类定义如下:
1 | package com.afei.jnidemo; |
JNI 调用如下:
1 | jclass clazz = env->FindClass("com/afei/jnidemo/Test"); |
签名时其中括号内是方法的参数,括号后是返回值类型。例如 show 方法,第一个参数是 String 类,对应 Ljava/lang/String;(注意后面有一个分号),第二个参数是 int 基本类型,对应的类型描述符是 I,返回值也是 int,同样是 I,所以最终该方法的签名为 “(Ljava/lang/String;I)I”。
NativeType CallMethod(jobject obj, jmethodID methodID, …)
NativeType CallMethodA(jobject obj, jmethodID methodID, jvalue* args)
NativeType CallMethodV(jobject obj, jmethodID methodID, va_list args)
说明:调用对象的某个方法,唯一不同的是输入参数的传入形式不同,这里 type 表示的是一系列方法,如下:
参数:
obj:某个 Java 对象实例
methodID:指定方法的ID
args:输入参数列表
示例:1
2
3jclass 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);
jmethodID GetStaticMethodID(jclass clazz, const char name, const char sig)
说明:同 GetMethodID,只不过操作的是静态方法NativeType CallStaticMethod(jclass clazz, jmethodID methodID, …)
NativeType CallStaticMethodA(jclass clazz, jmethodID methodID, jvalue* args)
NativeType CallStaticMethodV(jclass clazz, jmethodID methodID, va_list args)
说明:同 NativeType CallMethod,只不过操作的是静态方法,参数也由 jobject 变成了 jclass。
七、全局引用和局部引用
jobject NewGlobalRef(jobject obj)
说明:创建一个全局引用,不用时必须调用 DeleteGlobalRef() 方法释放。
参数:
obj:某个 Java 对象实例,可以是局部引用或全局引用
示例:1
2
3
4
5jclass mPointFClass; // global reference to PointF class
...
jclass clazz = env->FindClass("android/graphics/PointF");
mPointFClass = (jclass) env->NewGlobalRef(clazz);
void DeleteGlobalRef(jobject globalRef)
说明:释放某个全局引用
参数:
- globalRef:某全局引用
jobject NewLocalRef(jobject ref)
说明:创建一个局部引用。这个方法一般很少用。
参数:
- ref:某个引用,可以是全局引用或者局部引用
void DeleteLocalRef(jobject localRef)
说明:释放某个局部引用
参数:
localRef:某局部引用
注意:
局部引用在方法执行完后也会自动释放,不过当你在执行一个很大的循环时,里面会产生大量临时的局部引用,那么建议的做法是手动的调用该方法去释放这个局部引用。
jweak NewWeakGlobalRef(jobject obj)
说明:创建一个全局的弱引用
参数:
obj:某个 Java 对象实例
注意:
弱引用不会阻止 GC 回收它引用的对象,在内存不足时,弱引用的对象往往会被回收掉,使用时一定要多加小心。
void DeleteWeakGlobalRef(jweak obj)
说明:释放某个全局的弱引用
参数:
- obj:某个全局弱引用
八、字符串操作
jstring NewString(const jchar* unicodeChars, jsize len)
说明:以 UTF-16 的编码方式创建一个 Java 的字符串(jchar 的定义为 uint16_t)
参数:
- unicodeChars:指向字符数组的指针
- len:字符数组的长度
jstring NewStringUTF(const char* bytes)
说明:以 UTF-8 的编码方式创建一个 Java 的字符串
参数:
- bytes:指向字符数组的指针
jsize GetStringLength(jstring string)
jsize GetStringUTFLength(jstring string)
说明:获取字符串的长度,GetStringLength 是 UTF-16 编码,GetStringUTFLength 是 UTF-8 编码
参数:
- string:字符串
const jchar GetStringChars(jstring string, jboolean isCopy)
const char GetStringUTFChars(jstring string, jboolean isCopy)
说明:将 Java 风格的 jstring 对象转换成 C 风格的字符串,同上一个是 UTF-16 编码,一个是 UTF-8 编码
参数:
- string:Java 风格的字符串
- isCopy:是否进行拷贝操作,0 为不拷贝
void ReleaseStringChars(jstring string, const jchar* chars)
void ReleaseStringUTFChars(jstring string, const char* utf)
说明:释放指定的字符串指针,通常来说,Get 和 Release 是成对出现的
参数:
string:Java 风格的字符串
chars/utf:对应的 C 风格的字符串
示例:1
2
3
4
5
6JNIEXPORT 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);
}
九、数组操作
jobjectArray NewObjectArray(jsize length, jclass elementClass, jobject initialElement)
说明:创建引用数据类型的数组
参数:
length:数组的长度
elementClass:数组的元素所属的类
initialElement:使用什么样的对象来初始化,可以选择 NULL
示例:1
2
3int points_count = 21;
jclass pointFClass = env->FindClass("android/graphics/PointF");
jobjectArray point_array = env->NewObjectArray(points_count, pointFClass, NULL);
2.ArrayType NewArray(jsize length)
说明:创建基本数据类型的数组。这里的基本数据类型有:
参数:
- length:数组的长度
jsize GetArrayLength(jarray array)
说明:获取数组的长度
参数:
- array:指定的数组对象。jarray 是 jbooleanArray、jbyteArray、jcharArray 等的父类。
jobject GetObjectArrayElement(jobjectArray array, jsize index)
说明:获取引用数据类型数组指定索引位置处的对象
参数:
- array:引用数据类型数组
- index:目标索引值
void SetObjectArrayElement(jobjectArray array, jsize index, jobject value)
说明:设置引用数据类型数组指定索引位置处的值
参数:
- array:需要设置的引用数据类型数组
- index:目标索引值
- value:需要设置的值
NativeType GetArrayElements(ArrayType array, jboolean isCopy)
说明:获取基本数据类型数组的头指针
参数:
- array:基本数据类型数组
- isCopy:是否进行拷贝操作,0 为不拷贝
void ReleaseArrayElements(ArrayType array, NativeType* elems, jint mode)
说明:释放基本数据类型数组指针。通常来说,Get 和 Release 是成对出现的
参数:
- array:基本数据类型数组
- elems:对应的 C 风格的基本数据类型指针
- mode:释放模式,通常我们都是使用 0,有三种,如下
示例:
1 | Java_com_afei_jnidemo_MainActivity_test(JNIEnv *env, jobject instance, jintArray array_) { |
void GetArrayRegion(ArrayType array, jsize start, jsize len, NativeType* buf)
说明:返回基本数据类型数组的部分副本。这里的基本数据类型有:
参数:
- array:基本数据类型数组
- start:起始的索引值
- len:拷贝的长度
- buf:拷贝到的目标数组
void SetArrayRegion(ArrayType array, jsize start, jsize len, const NativeType* buf)
说明:设置基本数据类型数组元素。类型和上面的表类似。
参数:
- array:需要设置的基本数据类型数组
- start:起始的索引值
- len:需要设置的 buf 的长度
- buf:需要设置的值数组
十、异常操作
jint Throw(jthrowable obj)
说明:抛出一个异常,需要手动创建异常的实例,调用较复杂,一般不使用这个方法
参数:
obj:异常对象
示例:1
2
3
4
5
6
7
8
9jclass 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 {
// 创建失败
}
jint ThrowNew(jclass clazz, const char* message)
说明:抛出一个异常。使用起来比三个方法方便
参数:
clazz:指定的异常类
message:异常信息
示例:if (env->ThrowNew(env->FindClass(“java/io/IOException”), “IO异常”) == JNI_OK) {
// 创建成功
} else {
// 创建失败
}
jthrowable ExceptionOccurred()
jboolean ExceptionCheck()
说明:检查是否有异常,如果本地函数有异常抛出,ExceptionOccurred 会返回这个异常的示例,ExceptionCheck 只返回是否有异常void ExceptionDescribe()
说明:将异常和堆栈信息推送到错误流void ExceptionClear()
说明:清除掉发生的异常
以上几个方法的示例:
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 部分代码为:
1 | JNIEXPORT void JNICALL |
运行结果为:
1 | 07-12 14:57:07.443 26623-26623/com.afei.jnidemo D/FaceAPI: ============ |
void FatalError(const char* msg)
说明:抛出一个致命异常,并且不希望JVM处理
参数:
- msg:致命异常的信息
十一、其它
其他还有有关 Monitor Operations、NIO Support、Reflection Support 等一些方法,由于我也没使用过,就不再这里解释了。
可以参考 JNI 的官网的介绍:https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp9502
原文链接:JNI 方法大全及使用示例-CSDN博客