在前面的两篇文章中,介绍了 Android 通过 JNI 进行基础类型、字符串和数组的相关操作,并描述了 Java 和 Native 在类型和签名之间的转换关系。

有了之前那些基础,就可以实现 Java 和 Native 的相互调用了,在 Native 中去访问 Java 类的字段并调用相应的方法。

访问字段

Native 方法访问 Java 的字段有两种形式,分别是访问类的实例字段和访问类的静态字段。

不管哪种操作,首先要定义一个具体的 Java 类型,其中,有实例的字段类型和方法,也有静态的字段类型和方法。

  1. public class Animal {
  2. protected String name;
  3. public static int num = 0;
  4. public Animal(String name) {
  5. this.name = name;
  6. }
  7. public String getName() {
  8. return this.name;
  9. }
  10. public int getNum() {
  11. return num;
  12. }
  13. }

访问类的实例字段

访问 Java 类的字段,大致步骤如下:

  1. 获取 Java 对象的类
  2. 获取对应字段的 id
  3. 获取具体的字段值

以访问以上 Animal 类的 name 字段,并将其修改为例:

  1. private native void accessInstanceFiled(Animal animal);

对应的 C++ 代码如下:

  1. extern "C"
  2. JNIEXPORT void JNICALL
  3. Java_com_glumes_cppso_jnioperations_FieldAndMethodOps_accessInstanceFiled(JNIEnv *env,jobject instance, jobject animal) {
  4. jfieldID fid; // 想要获取的字段 id
  5. jstring jstr; // 字段对应的具体的值
  6. const char *str; // 将 Java 的字符串转换为 Native 的字符串
  7. jclass cls = env->GetObjectClass(animal); // 获取 Java 对象的类
  8. fid = env->GetFieldID(cls, "name", "Ljava/lang/String;"); // 获取对应字段的 id
  9. if (fid == NULL) { // 如果字段为 NULL ,直接退出,查找失败
  10. return;
  11. }
  12. jstr = (jstring) env->GetObjectField(animal, fid); // 获取字段对应的值
  13. str = env->GetStringUTFChars(jstr, NULL);
  14. if (str == NULL) {
  15. return;
  16. }
  17. LOGD("name is %s", str);
  18. env->ReleaseStringUTFChars(jstr, str);
  19. jstr = env->NewStringUTF("replaced name");
  20. if (jstr == NULL) {
  21. return;
  22. }
  23. env->SetObjectField(animal, fid, jstr); // 修改字段对应的值
  24. }

在上面的代码中,首先通过 GetObjectClass 函数获取对应的 Java 类,其参数就是要获得的对象类型 jobject ,然后得到的结果就是一个 jclass 类型的值,代表 Java 的 Class 类型。

其次是通过 GetFieldID 方法获得 Java 类型对应的字段 id 。其中,第一个参数就是之前获得的 Java 类型,第二个参数就是在 Java 中字段的具体名字,第三个参数就是字段对应的具体类型,这个类型的签名描述要转换成 Native 的表示形式,也就是之前提到的 Java 和 Native 的签名转换。

得到了 Java 类型和字段的 id 后,就可以通过 GetObjectField 方法来获取具体的值,它的两个参数分别是之前获得的 Java 类型和字段 id 。

GetObjectField 方法有很多形态,对于字段值是引用类型的,统一是 GetObjectField,然后得到的结果转型为想要的类型。对于基础类型,则有则对应的方法,比如 GetBooleanFieldGetIntFieldGetDoubleField 等等。

得到了字段的值之后,就可以进行想要的操作了。

最后,还可以通过 SetObjectField 方法来修改字段对应的值。它的前两个参数也是对应的 Java 类型和字段 id,最后的参数则是具体的值,此方法也是针对于字段类型是引用类型,而对于基础类型,也有着对应的方法,比如 SetBooleanFieldSetCharFieldSetDoubleField

访问类的静态字段

访问类的静态字段,大致步骤和类的实例字段类似:

  1. private native void accessStaticField(Animal animal);

对应的 C++ 代码如下:

  1. extern "C"
  2. JNIEXPORT void JNICALL
  3. Java_com_glumes_cppso_jnioperations_FieldAndMethodOps_accessStaticField(JNIEnv *env, jobject instance,jobject animal) {
  4. jfieldID fid;
  5. jint num;
  6. jclass cls = env->GetObjectClass(animal);
  7. fid = env->GetStaticFieldID(cls, "num", "I");
  8. if (fid == NULL) {
  9. return;
  10. }
  11. num = env->GetStaticIntField(cls, fid);
  12. LOGD("get static field num is %d", num);
  13. env->SetStaticIntField(cls, fid, ++num);
  14. }

类的静态和实例字段的访问最大不同就在于,JNI 调用对应的方法不同。对于类的静态字段,JNI 的方法多了 Static 的标志来表明这个对应于类的静态字段访问。

方法调用

JNI 调用 Java 方法和 JNI 访问 Java 字段的步骤也大致相同,

  1. 获取 Java 对象的类
  2. 获取对应方法的 id
  3. 调用具体的方法

以调用类的实例方法和静态方法为例:

调用类的实例方法

JNI 调用 Java 类的实例方法

  1. private native void callInstanceMethod(Animal animal);

对应 C++ 代码如下:

  1. // Native 访问 Java 的类实例方法
  2. extern "C"
  3. JNIEXPORT void JNICALL
  4. Java_com_glumes_cppso_jnioperations_FieldAndMethodOps_callInstanceMethod(JNIEnv *env, jobject instance,jobject animal) {
  5. jclass cls = env->GetObjectClass(animal); // 获得具体的类
  6. jmethodID mid = env->GetMethodID(cls, "callInstanceMethod", "(I)V"); // 获得具体的方法 id
  7. if (mid == NULL) {
  8. return;
  9. }
  10. env->CallVoidMethod(animal, mid, 2); // 调用方法
  11. }

与访问字段不同的是,GetFieldID 方法换成了 GetMethodID 方法,另外由 CallVoidMethod 函数来调用具体的方法,前面两个参数是获得的类和方法 id,最后的参数是具体调用方法的参数。

GetMethodID 方法的第一个参数就是具体的 Java 类型,第二个参数是该 Java 类的对应实例方法的名称,第三个参数就是该方法对应的返回类型和参数签名转换成 Native 对应的描述。

对于不需要返回值的函数,调用 CallVoidMethod 即可,对于返回值为引用类型的,调用 CallObjectMethod 方法,对于返回基础类型的方法,则有各自对应的方法调用,比如:CallBooleanMethodCallShortMethodCallDoubleMethod 等等。

调用类的静态方法

对于调用类的静态方法和调用类的实例方法类似:

  1. private native void callStaticMethod(Animal animal);

对应 C++ 代码如下:

  1. // Native 访问 Java 的静态方法
  2. extern "C"
  3. JNIEXPORT void JNICALL
  4. Java_com_glumes_cppso_jnioperations_FieldAndMethodOps_callStaticMethod(JNIEnv *env,jobject instance, jobject animal) {
  5. jclass cls = env->GetObjectClass(animal);
  6. jmethodID argsmid = env->GetStaticMethodID(cls, "callStaticMethod",
  7. "(Ljava/lang/String;)Ljava/lang/String;");
  8. if (argsmid == NULL) {
  9. return;
  10. }
  11. jstring jstr = env->NewStringUTF("jstring");
  12. env->CallStaticObjectMethod(cls, argsmid, jstr);

调用类的静态方法 callStaticMethod,该方法需要传递一个 String 字符串参数,同时返回一个字符串参数。

具体的调用过程和调用类的实例方法类似,差别也只是在于调用方法名多加了一个 Static 的标识。

小结

可以看到,从 JNI 中访问 Java 的字段和访问,两者的步骤都是大致相似的,只是调用的 JNI 方法有所区别。

具体示例代码可参考我的 Github 项目,欢迎 Star。

https://github.com/glumes/AndroidDevWithCpp

Android 通过 JNI 访问 Java 字段和方法调用的更多相关文章

  1. Android JNI访问Java成员

    在 JNI 调用中,不仅仅 Java 可以调用本地方法,本地方法也可以调用 Java 中的方法和成员变量. Java 中的类封装了属性和方法,想要访问 Java 中的属性和方法,首先要获得 Java ...

  2. [Android]通过JNI访问并操作Bitmap的元素,支持RGB565和ARGB8888

    [Android]通过JNI访问并操作Bitmap的元素,支持RGB565和ARGB8888 标签: androidbitmapjni 2014-05-09 20:35 2985人阅读 评论(1) 收 ...

  3. JAVA中native方法调用

    在Java中native是关键字.它一般在本地声明,异地用C和C++来实现.它的声明有几点要注意:1)native与访问控制符前后的关系不受限制.2)必须在返回类型之前.3)它一般为非抽象类方法.4) ...

  4. 【Java基础】方法调用机制——MethodHandle

    MethodHandle是Java7引入的一种机制,主要是为了JVM支持动态语言. 一个MethodHandle调用示例 共有方法调用 首先,演示一下最基本的MethodHandle使用. 第一步:创 ...

  5. Android与JNI(二) ---- Java调用C++ 动态调用

    目录: 1. 简介 2. JNI 组件的入口函数 3. 使用 registerNativeMethods 方法 4. 测试 5. JNI 帮助方法 6. 参考资料 1. 简介 Android与JNI( ...

  6. Java RMI 远程方法调用

    Java RMI 指的是远程方法调用 (Remote Method Invocation).它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法.可以用此方 ...

  7. Java RMI远程方法调用

    RMI(远程接口调用) 1. RMI的原理: RMI系统结构,在客户端和服务器端都有几层结构. 方法调用从客户对象经占位程序(Stub).远程引用层(Remote Reference Layer)和传 ...

  8. Android中使用ContentProvider进行跨进程方法调用

    原文同一时候发表在我的博客 点我进入还能看到很多其它 需求背景 近期接到这样一个需求,须要和别的 App 进行联动交互,比方下载器 App 和桌面 App 进行联动.桌面的 App 能直接显示下载器 ...

  9. Android浏览器访问java web的方法

    以前自己也做过Android程序,可以和服务器通信,通过json来存取数据,当时是在APP中直接存取数据的,而这次我打算在手机浏览器中获得服务器传过来的Json参数,后来才发现其实很简单的,首先需要手 ...

随机推荐

  1. 禁止单个IP或ip段访问

    //IP禁止判断接口,返回true则为找到 function checkIp($ip, $ipbanned) { $ipbannedFlag = false; if (!empty($ipbanned ...

  2. MFC中的CString类使用方法指南

    MFC中的CString类使用方法指南 原文出处:codeproject:CString Management [禾路:这是一篇比较老的资料了,但是对于MFC的程序设计很有帮助.我们在MFC中使用字符 ...

  3. JAVA中拆箱和装箱

    浅谈JAVA中拆箱与装箱 一.  什么是装箱?什么是拆箱? 在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行: Integer i = new Integer(10) ...

  4. 变参标准函数的重新封装,如printf

    方法一: #include <stdio.h> #include <stdarg.h> void my_trace(const char *cmd, ...) { printf ...

  5. NOIP 2016 蚯蚓 (luogu 2827 & uoj 264) - 鬼畜的优化

    题目描述 本题中,我们将用符号\lfloor c \rfloor⌊c⌋表示对c向下取整,例如:\lfloor 3.0 \rfloor= \lfloor 3.1 \rfloor=\lfloor 3.9 ...

  6. 动态规划之97 Interleaving String

    题目链接:https://leetcode-cn.com/problems/interleaving-string/description/ 参考链接:https://blog.csdn.net/u0 ...

  7. topcoder srm 370 div1

    problem1 link 枚举每一种大于等于$n$的计算其概率即可. problem2 link 首先二分答案,然后计算.令$f[i][j]$表示移动完前$i$最后一个在位置$j$的最小代价. pr ...

  8. bzoj1458: 士兵占领 网络流

    链接 https://www.lydsy.com/JudgeOnline/problem.php?id=1458 也可以去luogu 思路 想成倒着删去点,使得依旧满足覆盖!! 左边横,右边列,之间用 ...

  9. 【第四十章】Spring Boot 自定义拦截器

    1.首先编写拦截器代码 package com.sarnath.interceptor; import javax.servlet.http.HttpServletRequest; import ja ...

  10. jvm:垃圾收集器

    垃圾收集器: Serial 收集器: 单线程收集器,专注做收集,会暂停别的工作.收集效果好. ParNew 收集器: 是Serial的多线程版本.目前只有它能和CMS收集器配合.    Paralle ...