原文出处:http://www.ccbu.cc/index.php/android/android-jni-object-operate.html
1. 对象操作基本步骤
Jni是沟通Java世界和Native世界的纽带,Java层调用本地方法只用调用Java中定义的本地(native
)方法就可用了,那么,本地的C/C++代码如何调用Java层的代码呢?这就是本章节对象操作
要解决阐述的内容。
一般的,C/C++层要调用Java层代码,需要进行以下步骤。
- 获取Java层对应的
jclass
,通过jclass
来获取Java类的方法,属性。 - 获取Java层对象引用,如果Java层有传引用下来,则可用直接使用,否则就需要在C/C++层调用创建对象的接口来创建Java层对应类的对象。调用Java静态函数不需要对象引用。
- 调用Java层类的静态方法,或者Java层对象的方法。
- 处理返回值。
2. 类操作
2.1 函数列表
Jni中类操作相关的函数包括以下函数:
函数 | 说明 |
---|---|
DefineClass | 从原始类数据的缓冲区中加载类 |
FindClass | 查找类 |
GetSuperclass | 查找父类 |
IsAssignableFrom | 类型判断 |
2.2 关于类名
Jni在加载或查找Java层类时,需要通过类的全名来进行加载,该类的全名为类包含包名的全路径,比如 java.lang.String
类,那么类的全名则为java/lang/String
;
2.3 对应的C++函数定义如下:
/**
* 从原始类数据的缓冲区中加载类
* @param name 类的全名,必须是被UTF-8编码过的字符串。
* @param loader 类加载器
* @param buf 包含 .class 文件数据的缓冲区
* @return java类对象。发生错误时返回NULL
*/
jclass DefineClass(const char *name, jobject loader, const jbyte* buf,
jsize bufLen)
/**
* 查找类
* @param name 类的全名,必须是被UTF-8编码过的字符串。
* @return
*/
jclass FindClass(const char* name)
/**
* 获取父类
* @param clazz Java类对象
* @return java类对象。clazz的父类或NULL
*/
jclass GetSuperclass(jclass clazz)
/**
* 确定clazz1的对象是否可安全地强制转换为clazz2
* @param clazz1 类的对象
* @param clazz2 类的对象
* @return 如果满足以下任一条件,则返回JNI_TRUE:
* 1. 如果clazz1和clazz2是同一个Java类。
* 2. 如果clazz1是clazz2的子类
* 3. 如果clazz1是clazz2接口的实现类
*/
jboolean IsAssignableFrom(jclass clazz1, jclass clazz2)
2.4 对应的C函数定义为:
jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*, jsize);
jclass (*FindClass)(JNIEnv*, const char*);
jclass (*GetSuperclass)(JNIEnv*, jclass);
jboolean (*IsAssignableFrom)(JNIEnv*, jclass, jclass);
jclass (*GetObjectClass)(JNIEnv*, jobject);
3. 对象操作
Jni中对象操作相关的函数主要包括:
函数 | 说明 |
---|---|
AllocObject | 直接创建Java对象 |
NewObject | 根据某个构造函数来创建Java对象 |
NewObjectV | 根据某个构造函数来创建Java对象 |
NewObjectA | 根据某个构造函数来创建Java对象 |
GetObjectClass | 获取指定类对象的类 |
IsInstanceOf | 判断某个对象是否是某个“类”的子类 |
对应的C++函数定义如下:
/**
* 不借助任何构造函数的情况下分配一个新的Java对象
* @param clazz Java类对象
* @return 返回一个Java对象实例,如果该对象无法被创建,则返回NULL
*/
jobject AllocObject(jclass clazz)
/**
* 根据构造函数来创建Java对象
* @param clazz Java类对象
* @param methodID 构造函数的方法ID
* @param ... 构造函数的输入参数(可变参数)
* @return 返回一个Java对象实例,如果无法创建该对象,则返回NULL
*/
jobject NewObject(jclass clazz, jmethodID methodID, ...)
/**
* 根据构造函数来创建Java对象
* @param clazz Java类对象
* @param methodID 构造函数的方法ID
* @param args 构造函数的输入参数(va_list)
* @return 返回一个Java对象实例,如果无法创建该对象,则返回NULL
*/
jobject NewObjectV(jclass clazz, jmethodID methodID, va_list args)
/**
* 根据构造函数来创建Java对象
* @param clazz Java类对象
* @param methodID 构造函数的方法ID
* @param args 构造函数的输入参数(参数数组)
* @return 返回一个Java对象实例,如果无法创建该对象,则返回NULL
*/
jobject NewObjectA(jclass clazz, jmethodID methodID, const jvalue* args)
/**
* 通过对象获取类
* @param obj 类的对象
* @return Java 类对象。
*/
jclass GetObjectClass(jobject obj)
/**
* 判断某个对象是否是某个“类”的子类
* @param obj Java对象实例
* @param clazz Java类对象
* @return
*/
jboolean IsInstanceOf(jobject obj, jclass clazz)
对应的C函数定义为:
jobject (*AllocObject)(JNIEnv*, jclass);
jobject (*NewObject)(JNIEnv*, jclass, jmethodID, ...);
jobject (*NewObjectV)(JNIEnv*, jclass, jmethodID, va_list);
jobject (*NewObjectA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jclass (*GetObjectClass)(JNIEnv*, jobject);
jboolean (*IsInstanceOf)(JNIEnv*, jobject, jclass);
4. 获取方法
本地C/C++代码中,可用同类对象jclass来获取该Java类的方法,主要包括获取类方法和获取静态方法。
函数 | 说明 |
---|---|
GetMethodID | 获取类方法 |
GetStaticMethodID | 获取类的静态方法 |
对应的C++函数定义如下:
/**
* 获取指定类的类方法
* @param clazz Java类对象
* @param name 方法名,必须为"utf-8"的字符串
* @param sig 方法签名,必须为"utf-8"的字符串
* @return 返回对应的方法ID,没有找到指定的方法,则返回NULL
*/
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
/**
* 获取指定类的静态方法
* @param clazz Java类对象
* @param name 方法名,必须为"utf-8"的字符串
* @param sig 方法签名,必须为"utf-8"的字符串
* @return 返回对应的方法ID,没有找到指定的方法,则返回NULL
*/
jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)
对应的C函数定义为:
jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
5. 类方法调用
Jni中本地函数中回调Java类方法通过调用Call
函数 | 参数类型 | 参数说明 |
---|---|---|
get |
... | 可边长参数 |
get |
va_list | va_list类型参数 |
get |
const jvalue* | 数组形式的参数 |
以CallObjectMethod函数为列,看一下其函数原型。
// C 函数原型
jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
jobject (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jobject (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
// C++ 函数原型
jobject CallObjectMethod(jobject, jmethodID, ...);
jobject CallObjectMethodV(jobject, jmethodID, va_list);
jobject CallObjectMethodA(jobject, jmethodID, const jvalue*);
类方法调用的Call
函数 | 返回值类型 |
---|---|
CallObjectMethod CallObjectMethodV CallObjectMethodA |
jobject |
CallBooleanMethod CallBooleanMethodV CallBooleanMethodA |
jboolean |
CallByteMethod CallByteMethodV CallByteMethodA |
jbyte |
CallCharMethod CallCharMethodV CallCharMethodA |
jchar |
CallShortMethod CallShortMethodV CallShortMethodA |
jshort |
CallIntMethod CallIntMethodV CallIntMethodA |
jint |
CallLongMethod CallLongMethodV CallLongMethodA |
jlong |
CallFloatMethod CallFloatMethodV CallFloatMethodA |
jfloat |
CallDoubleMethod CallDoubleMethodV CallDoubleMethodA |
jdouble |
CallVoidMethod CallVoidMethodV CallVoidMethodA |
void |
6.类的静态方法调用
Jni中本地函数中回调Java类的静态方法的方式和访问类方法的类似。唯一不同的是,调用类的静态方法时不用传入Java类对象引用。Call
函数 | 返回值类型 |
---|---|
CallObjectStaticMethod CallObjectStaticMethodV CallObjectStaticMethodA |
jobject |
CallBooleanStaticMethod CallBooleanStaticMethodV CallBooleanStaticMethodA |
jboolean |
CallByteStaticMethod CallByteStaticMethodV CallByteStaticMethodA |
jbyte |
CallCharStaticMethod CallCharStaticMethodV CallCharStaticMethodA |
jchar |
CallShortStaticMethod CallShortStaticMethodV CallShortStaticMethodA |
jshort |
CallIntStaticMethod CallIntStaticMethodV CallIntStaticMethodA |
jint |
CallLongStaticMethod CallLongStaticMethodV CallLongStaticMethodA |
jlong |
CallFloatStaticMethod CallFloatStaticMethodV CallFloatStaticMethodA |
jfloat |
CallDoubleStaticMethod CallDoubleStaticMethodV CallDoubleStaticMethodA |
jdouble |
CallVoidStaticMethod CallVoidStaticMethodV CallVoidStaticMethodA |
void |