望远山,知近路,而后自得其乐!

Android JNI开发详解(6)-对象操作

1. 对象操作基本步骤

Jni是沟通Java世界和Native世界的纽带,Java层调用本地方法只用调用Java中定义的本地(native)方法就可用了,那么,本地的C/C++代码如何调用Java层的代码呢?这就是本章节对象操作要解决阐述的内容。

一般的,C/C++层要调用Java层代码,需要进行以下步骤。

  1. 获取Java层对应的 jclass ,通过 jclass 来获取Java类的方法,属性。
  2. 获取Java层对象引用,如果Java层有传引用下来,则可用直接使用,否则就需要在C/C++层调用创建对象的接口来创建Java层对应类的对象。调用Java静态函数不需要对象引用。
  3. 调用Java层类的静态方法,或者Java层对象的方法。
  4. 处理返回值。

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<PrimitiveType>Method系列函数实现。Call<PrimitiveType>Method系列函数,根据传入参数的部分,分为Call<PrimitiveType>Method, Call<PrimitiveType>MethodV和Call<PrimitiveType>MethodA三类函数,参数区别如下:

函数参数类型参数说明
get<PrimitiveType>Method...可边长参数
get<PrimitiveType>MethodVva_listva_list类型参数
get<PrimitiveType>MethodAconst 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<PrimitiveType>Method函数主要根据返回值类型的不同进行了分类,主要包括Object类型返回值的函数调用和基本数据类型返回值的函数调用。函数列表如下:

函数返回值类型
CallObjectMethod<br/>CallObjectMethodV<br/>CallObjectMethodAjobject
CallBooleanMethod<br/>CallBooleanMethodV<br/>CallBooleanMethodAjboolean
CallByteMethod<br/>CallByteMethodV<br/>CallByteMethodAjbyte
CallCharMethod<br/>CallCharMethodV<br/>CallCharMethodAjchar
CallShortMethod<br/>CallShortMethodV<br/>CallShortMethodAjshort
CallIntMethod<br/>CallIntMethodV<br/>CallIntMethodAjint
CallLongMethod<br/>CallLongMethodV<br/>CallLongMethodAjlong
CallFloatMethod<br/>CallFloatMethodV<br/>CallFloatMethodAjfloat
CallDoubleMethod<br/>CallDoubleMethodV<br/>CallDoubleMethodAjdouble
CallVoidMethod<br/>CallVoidMethodV<br/>CallVoidMethodAvoid


6.类的静态方法调用

Jni中本地函数中回调Java类的静态方法的方式和访问类方法的类似。唯一不同的是,调用类的静态方法时不用传入Java类对象引用。Call<PrimitiveType>StaticMethod系列函数列表如下:

函数返回值类型
CallObjectStaticMethod<br/>CallObjectStaticMethodV<br/>CallObjectStaticMethodAjobject
CallBooleanStaticMethod<br/>CallBooleanStaticMethodV<br/>CallBooleanStaticMethodAjboolean
CallByteStaticMethod<br/>CallByteStaticMethodV<br/>CallByteStaticMethodAjbyte
CallCharStaticMethod<br/>CallCharStaticMethodV<br/>CallCharStaticMethodAjchar
CallShortStaticMethod<br/>CallShortStaticMethodV<br/>CallShortStaticMethodAjshort
CallIntStaticMethod<br/>CallIntStaticMethodV<br/>CallIntStaticMethodAjint
CallLongStaticMethod<br/>CallLongStaticMethodV<br/>CallLongStaticMethodAjlong
CallFloatStaticMethod<br/>CallFloatStaticMethodV<br/>CallFloatStaticMethodAjfloat
CallDoubleStaticMethod<br/>CallDoubleStaticMethodV<br/>CallDoubleStaticMethodAjdouble
CallVoidStaticMethod<br/>CallVoidStaticMethodV<br/>CallVoidStaticMethodAvoid

文章评论已关闭!