文档章节

在JNI中自C++回调Java

DDL007
 DDL007
发布于 2017/05/24 21:25
字数 934
阅读 74
收藏 1

1. 开发环境

  • MyEclipse 2016 CI
  • Visual Studio 2010
  • JDK 1.8_45 x64

2. 编写Java类,将需要在C/C++中实现的部分方法使用native关键字修饰

FCClient.java

package com.mlight.floorcontrol.client;

/**
 * @author ddl007
 */
public class FCClient {
	/**
	 * 设置服务器信息
	 * 
	 * @param ip
	 * @param port
	 *            以秒为单位
	 */
	public native void init(String ip, int port);

	/**
	 * 发送消息
	 * 
	 * @param jsonMsg
	 */
	public native void sendMsg(String jsonMsg);

	/**
	 * 响应通知
	 * 
	 * @param listener
	 *            消息通知
	 */
	public native void onMsg(FCListener listener);

	static {
		System.loadLibrary("JniSdk");
	}

	public static void main(String[] args) throws InterruptedException {
		FCListener listener = new FCListener() {

			@Override
			public void onMessage(String json) {
				System.out.println(json);
			}
		};

		FCClient client = new FCClient();
		client.init("192.168.2.190", 6980);
		client.sendMsg("{\"msg\":\"jsonMsg\"}");
		client.onMsg(listener);
	}
}

 

MessageListener.java

package com.mlight.floorcontrol.client;

/**
 * 处理通知信息
 * 
 * @author ddl007
 */
public interface FCListener {
	public void onMessage(String json);
}

2. 使用javac编译,当然此处为集成开发环境,可跳过。如果使用javac编译,命令如下

javac -classpath . -encoding utf-8 com\mlight\floorcontrol\client\FCClient.java

3. 使用javah生成jni头文件,com_mlight_floorcontrol_client_FCClient.h

javah com.mlight.floorcontrol.client.FCClient

4.在VS2010下新建dll工程clientSDK,并将com_mlight_floorcontrol_client_FCClient.h拷贝到头文件夹下

5.设置工程的“包含目录”(右键工程>属性>VC++目录>包含目录),增加如下两个路径

C:\Program Files\Java\jdk1.8.0_45\include\win32;C:\Program Files\Java\jdk1.8.0_45\include

6.设置工程的“库目录”(右键工程>属性>VC++目录>库目录),增加如下路径:

C:\Program Files\Java\jdk1.8.0_45\lib

7.在clientSDK.cpp中实现必要的逻辑,样例代码如下:

#include "stdafx.h"
#include "com_mlight_floorcontrol_client_FCClient.h"
#include <jni.h>
#include <iostream>

using namespace std;

char* jstringTostring(JNIEnv* env, jstring jstr)
{
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("utf-8");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
if (alen > 0)
{
rtn = (char*)malloc(alen + 1);

memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
env->ReleaseByteArrayElements(barr, ba, 0);
return rtn;
}

jstring stoJstring(JNIEnv* env, const char* pat)
{
jclass strClass = env->FindClass("Ljava/lang/String;");
jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
jbyteArray bytes = env->NewByteArray(strlen(pat));
env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);
jstring encoding = env->NewStringUTF("utf-8");
return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);
}

/*
 * Class:     com_mlight_floorcontrol_client_FCClient
 * Method:    init
 * Signature: (Ljava/lang/String;I)V
 */
JNIEXPORT void JNICALL Java_com_mlight_floorcontrol_client_FCClient_init
	(JNIEnv *env, jobject obj, jstring server, jint port){
	cout << "Java_com_mlight_floorcontrol_client_FCClient_init" << endl;
	cout << "server:" << jstringTostring(env,server) << endl;
	cout << "port:" << port << endl;
}

/*
 * Class:     com_mlight_floorcontrol_client_FCClient
 * Method:    sendMsg
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_mlight_floorcontrol_client_FCClient_sendMsg
	(JNIEnv *env, jobject, jstring msg){
	cout << "Java_com_mlight_floorcontrol_client_FCClient_sendMsg" << endl;
	cout << "msg:" << jstringTostring(env,msg) << endl;
}

/*
 * Class:     com_mlight_floorcontrol_client_FCClient
 * Method:    onMsg
 * Signature: (Lcom/mlight/floorcontrol/client/FCListener;)V
 */
JNIEXPORT void JNICALL Java_com_mlight_floorcontrol_client_FCClient_onMsg
	(JNIEnv *env, jobject, jobject listener){
	jclass cls = env->GetObjectClass(listener);
	jmethodID method = env->GetMethodID(cls,"onMessage","(Ljava/lang/String;)V");
	char paramStr[] = "abcdeasdfasdfasf";
	env->CallVoidMethod(listener,method,stoJstring(env,paramStr));
}

其中有2个地方是需要注意

  • jni生成的头文件中字符串类型为jstring,C++中字符串使用char *表示,两者需要转换后才能使用。转换过程参见jstringTostring,sttoJstring两个函数,其中也说明字节在C++和Java中的表示方式是一致的。
  • 在函数Java_com_mlight_floorcontrol_client_FCClient_onMsg
      (JNIEnv *, jobject, jobject);中调用onMessage函数,需要用到FCListener中onMessage方法的签名获取,可通过如下命令获得
    javap -s -p com.mlight.floorcontrol.client.FCListener

    运行结果如下

    Compiled from "FCListener.java"
    public interface com.mlight.floorcontrol.client.FCListener {
      public abstract void onMessage(java.lang.String);
        Signature: (Ljava/lang/String;)V
    }

     

  • 在clientSDK.cpp文件中调用FCListener的onMessage函数时,参数直接使用C++中双引号包围的字符串在编译时是没有问题的,但在运行时会出现进行崩溃。曾听说在使用JNI时,从C++中回调Java的方法会导致进程崩溃,本人也一直没有办法解决。但是回到强类型语言的基本要求,严格遵守类型约定。于是虚拟机进程不再崩溃。

8. 总结

  • 强类型语言要严格遵守类型要求;
  • 跨语言编程时,一定要清楚当前被操作对象的语言环境,以及语言环境本身给我们的制约;
  • 魔鬼藏在细节里,如果不该错的错了,那么一定有魔鬼藏在了我们的视线外;

© 著作权归作者所有

共有 人打赏支持
DDL007
粉丝 0
博文 5
码字总数 5104
作品 0
普陀
程序员
私信 提问
cocos2d-x中通过Jni实现Java与C++的互相调用

cocos2d-x中通过Jni实现Java与C++的互相调用。 cocos2d-x用开发者提供了一个类JniHelper,提供了java与c++之间互调的jni解决方案。 笔者所开发的“史上最坑爹的游戏”项目中使用到了JNI,为此...

MingliC
2013/12/23
0
2
JNI和NDK的区别

NDK(Native Development Kit)“原生”也就是二进制 android常用的开发方式是java封装的库,而这些库的底层实现是由C/C++实现,如媒体,图形库等 java调用这样实现就需要用JNI(Java Native...

长平狐
2013/01/06
62
0
C++与java混合编写和C++与OC混合编写 笔记

1、Java与C++混合编写: 使用JNI(Java本地调用) 实例:cocos2dx/platform/android/jni对应目录cocos2dx/platform/android/java const char getConstStrRetuen(const char funcName){ JniMeth......

悠米海
2014/06/12
0
0
java jni in ubuntu(1)

初次在ubuntu13.04上实验java jni的调用,弄的比较蛋疼,在此记录下. 先建立一个java工程. 贴上java代码 public class JNITest1 { static{System.load("/home/origin/workspace1/javajnitest1......

origin
2013/05/27
0
0
Java程序员从笨鸟到菜鸟之(九十六)深入java虚拟机(五)——java本地接口JNI详解

对于java程序员来说,java语言的好处和优点,我想不用我说了,大家自然会说出很多一套套的。但虽然我们作为java程序员,但我们不得不承认java语言也有一些它本身的缺点。比如在性能、和底层打...

长平狐
2012/11/12
121
0

没有更多内容

加载失败,请刷新页面

加载更多

tac 与cat

tac从后往前看文件,结合grep使用

writeademo
35分钟前
2
0
表单中readonly和dsabled的区别

这两种写法都会使显示出来的文本框不能输入文字, 但disabled会使文本框变灰,而且通过通过表单提交时,获取不到文本框中的value值(如果有的话), 而readonly只是使文本框不能输入,外观没...

少年已不再年少
57分钟前
2
0
SpringBoot上传图片操作

首先有个上传文件的工具类 /** * 文件上传 * @param file * @param filePath * @param fileName * @throws Exception */public static void uploadFile(byte[] file, String ...

_liucui_
今天
6
0
DrawerLayout

public class MainActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener,OnFragmentInteractionListener{ public NavigationView navView; ......

安卓工程师王恒
今天
1
0
python精简笔记

python精简笔记-字符串基本用法 字符串常见用法: * encode() # 编码成bytes类型 * find() # 查找子串 * index() # 获取下标 * replace() # 替换子串 * len(string) # 返回字符串长度,...

平头哥-Enjoystudy
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部