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

原文出处: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层代码,需要进行以下步骤。

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

函数 返回值类型
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<PrimitiveType>StaticMethod系列函数列表如下:

函数 返回值类型
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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容