最近在搞JNI那块,发现网上很多都是Java调用JNI,然后再调用C++的方法。而当C++函数里调用Java的方法,网上的文章可以说是少之又少,所以写此篇文章共勉。。。。

本文介绍两种方法,一是C++主动调用Java的情况;另一种是Java调用了C++,然后在该调用的C++里又回调另外的一个Java方法。其实这两种方法(或其他方法),都是要用到 JNIEnv,有关JNI的讲解可查阅此文章https://blog.csdn.net/tom_221x/article/details/69215286   在此感谢原文作者。

首先讲解本文介绍的第二种方法:Java调用C++,然后C++再回调另一个Java方法,直接上代码吧:

JavaVM* jvm = NULL;
jclass myClass = NULL;
jclass global_class = NULL;
jmethodID mid_method;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm,void* reserved){if (vm == NULL){return JNI_ERR;}JNIEnv *env = NULL;jvm = vm;if(vm->GetEnv((void**)&env,JNI_VERSION_1_4)!=JNI_OK){return JNI_ERR;}myClass = (env)->FindClass("com/shizhuangyuan/mycpp/MainActivity");global_class = (jclass)env->NewGlobalRef(myClass);mid_method = (env)->GetMethodID(global_class,"cpp2JavaTest","(I)V");return JNI_VERSION_1_4;
}

这里讲解下JNI_OnLoad这个函数:

当Android的VM(Virtual Machine)执行到C组件(即*so档)里的System.loadLibrary()函数时,首先会去执行C组件里JNI_OnLoad()函数。
它的用途有二: 
1、告诉VM此C组件使用那一个JNI版本。
      如果你的*.so档没有提供JNI_OnLoad()函数,VM会默认该*.so档是使用最老的JNI 1.1版本。
      由于新版的JNI做了许多扩充,如果需要使用JNI的新版功能,
      例如JNI 1.4的java.nio.ByteBuffer,就必须藉由JNI_OnLoad()函数来告知VM。

2、由于VM执行到System.loadLibrary()函数时,就会立即先呼叫JNI_OnLoad(),
      所以C组件的开发者可以藉由JNI_OnLoad()来进行C组件内的初期值之设定(Initialization) 。

在JNI_OnLoad函数里,首先通过FindClass把Java里的类找出来,接着GetMethodID找到需要调用的Java方法。第二个参数是该Java方法名。这里解析下第三个参数"(I)V"的含义:表示形参为int型,返回值为Void型的一个Java方法,详细的类型符号对照表如下所示:

Java类型 对应签名符号
Integer I
Short S
Char C
Long J
Float F
Double D
Byte B
Boolean Z
Void V
数组 [内部类型
Object对象 L开头,包名/类名,”;”结尾,$标识嵌套类

下面举几个例子吧:

void javaDemo1(int a, int b)            //(II)Vdouble javaDemo2(string a, int b)       //(Ljava/lang/String;I)Dvoid javaDemo3()                        //()Vstring javaDemo4(string[] a, boolean b) //([java/lang/String;Z)Ljava/lang/String;

当虚拟机释放该C库时,则会调用JNI_OnUnload()函数来进行善后清除动作。代码如下:

JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {JNIEnv *env = NULL;if (jvm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {return;}env->DeleteGlobalRef(global_class);return;
}

接着便是C++调用Java方法的核心代码了:

void cpp2jni(int msg){JNIEnv *env = NULL;if (jvm->AttachCurrentThread(&env, NULL))将当前线程注册到虚拟机中{return;}//实例化该类jobject jobject = env->AllocObject(global_class);//分配新 Java 对象而不调用该对象的任何构造函数。返回该对象的引用。//调用Java方法(env)->CallVoidMethod(jobject, mid_method,msg);jvm->DetachCurrentThread();
}

开开心运行代码,但是会出现一个运行时错误:

Thread[1,tid=9643,Native,Thread*=0x7f7ec96a00,peer=0x74e4ea30,"main"] attempting to detach while still running code

这是因为本例子是直接通过Java调用JNI里的函数,然后在JNI里调用C++函数,最后在该C++函数里调Java,即Java---JNI---C++---Java。

上述出现错误的原因是DetachCurrentThread()时报的错,调用DetachCurrentThread函数的地方在java线程中,即在java调用C++代码时在C++代码中调用了AttachCurrentThread方法来获取JNIEnv,此时JNIEnv已经通过参数传递进来,你不需要再次AttachCurrentThread来获取。在释放时就会报错。 

所以上述方便适用于:C++直接调用Java方法。

但如果通过Java调用了C++,接着直接利用C++回调Java方法,代码可以修改成这样:

void cpp2jni(int msg){JNIEnv *env = NULL;int status;bool isAttached = false;status = jvm->GetEnv((void**)&env, JNI_VERSION_1_4);if (status < 0) {if (jvm->AttachCurrentThread(&env, NULL))将当前线程注册到虚拟机中{return;}isAttached = true;}//实例化该类jobject jobject = env->AllocObject(global_class);//分配新 Java 对象而不调用该对象的任何构造函数。返回该对象的引用。//调用Java方法(env)->CallVoidMethod(jobject, mid_method,msg);if (isAttached) {jvm->DetachCurrentThread();}
}

其实就是在第一种方法加些判断就可以了。

本文最后,直接附上本项目的Demo代码吧:

github地址:https://github.com/Liangzhuhua/MyCPP.git

CSDN地址:https://download.csdn.net/download/toyauko/10726905

C++调用Java方法相关推荐

  1. 【Android NDK 开发】JNI 方法解析 ( C/C++ 调用 Java 方法 | 函数签名 | 调用对象方法 | 调用静态方法 )

    文章目录 I . 调用 Java 方法流程 II . 获取 jclass 对象 ( GetObjectClass ) III . 获取 jclass 对象 ( FindClass ) IV . JNI ...

  2. c 调用java post方法_C#调用Java方法(详细实例)

    C#可以直接引用C++的DLL和转换JAVA写好的程序.最近由于工作原因接触这方面比较多,根据实际需求,我们通过一个具体例子把一个JAVA方法转换成可以由C#直接调用的DLL C#调用c++ C#调用 ...

  3. JNI基础 c语言调用java方法

    利用c语言调用java无参的方法 java方法 com.example.jniparsedata.ParseData类中的方法     //打印     public void sayHelloFro ...

  4. 本地方法(JNI)——调用 java 方法

    [0]README 1) 本文部分文字描述 转自 core java volume 2 , 旨在理解 本地方法(JNI)--调用 java 方法 的基础知识 : 2) C语言调用java 方法,包括: ...

  5. xsl调用java方法传参_Java中的XSL转换:一种简单的方法

    xsl调用java方法传参 XSL转换 (XSLT)是将一个XML文档转换为另一个XML文档的强大机制. 但是,在Java中,XML操作相当冗长和复杂. 即使是简单的XSL转换,也必须编写几十行代码- ...

  6. android jni 结构体_Android应用开发Android JNI-c/c++调用java方法

    本文将带你了解Android应用开发Android JNI-c/c++调用java方法,希望本文对大家学Android有所帮助. " Android   JNI-c/c++调用java方法, ...

  7. java调用el_[Java教程][javaEE] EL表达式调用java方法

    [Java教程][javaEE] EL表达式调用java方法 0 2016-07-03 18:00:03 1.新建个类,类里面定义静态方法 package com.tsh.utils;import j ...

  8. C#调用Java方法(详细实例)

    C#可以直接引用C++的DLL和转换JAVA写好的程序.最近由于工作原因接触这方面比较多,根据实际需求,我们通过一个具体例子把一个JAVA方法转换成可以由C#直接调用的DLL C#调用c++ C#调用 ...

  9. js调用java_Js调用Java方法并互相传参的简单实例

    Js通过PhoneGap调用Java方法并互相传参的. 一.JAVA代码 写一个类,该类继承自Plugin并重写execute方法. public class PluginTest extends P ...

  10. cocos2dx在Android studio运行 以及在 Android 平台上使用 JavaScript 直接调用 Java 方法

    cocos2dx在Android studio运行 以及在 Android 平台上使用 JavaScript 直接调用 Java 方法 cocos2dx在Android studio运行 使用Andr ...

最新文章

  1. python使用教程cmd啥意思-Python 中的cmd模块学习
  2. vim 打开中文乱码
  3. java模拟servlet_Java应用程序模拟向Servlet发送POST请求
  4. 企业应用的Ant模组编译环境
  5. 【顶会论文解析】罪行预测
  6. 如何打开屏幕坏的手机_手机屏幕坏了怎么打开usb调试
  7. 伍德里奇 第6版 计量经济学导论_伍德里奇《计量经济学导论》第6版课后习题答案...
  8. mysql不是内部命令_mysql不是内部命令的错误解决方案
  9. 中西方关于颜色的理解对比
  10. 慕尼黑工业大学计算机博士申请条件,德国慕尼黑工业大学博士生申请条件
  11. 使用 Cloudreve 快速搭建一个强大的个人云盘
  12. 活用lambda之list函数处理
  13. O2O优惠券使用数据分析
  14. Multisim基础 NPN型三极管 简单放大电路示例
  15. 一位女程序员的奋斗路程
  16. 面试时怎样做精彩的自我介绍
  17. 神经网络学习小记录45——Keras常用学习率下降方式汇总
  18. 北京大学软微第二学位可能被取消!高校第二学士学位将成历史!
  19. R语言替换某一列中某个特定数值
  20. 99.9%的努力毁于0.1%的失误

热门文章

  1. 印度帮是如何统治硅谷的?
  2. mongoose自建_id
  3. A - TOYS(POJ - 2318) 计算几何的一道基础题
  4. 安全即服务 公共WiFi新格局显端倪
  5. CVE-2018-8174漏洞复现及内网渗透攻击
  6. 前端——JS之定时器的小案例(1)
  7. 库文件(动态库和静态库)
  8. python官网在哪里下载64bit_WinPython下载64bit
  9. 当你使用计算机时 首先,安全知识竞赛第一环节题库
  10. VMware16虚拟机安装macOS Monterey 12详细教程