文档章节

在JNI中自C++回调Java

DDL007
 DDL007
发布于 2017/05/24 21:25
字数 934
阅读 44
收藏 1
点赞 0
评论 0

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
普陀
程序员
android开发教程(3)— jni编程之采用SWIG从Java调用C/C++

Android 从Java调用C/C++ 当无法用 Java 语言编写整个应用程序时,JNI 允许您调用C/C++本机代码。在下列典型情况下,您可能决定使用本机代码: 希望用更低级、更快的编程语言C/C++去实现对时...

刘小米
2014/09/16
0
0
用 JNI 进行 Java 编程(2)

从 Java 程序调用 C/C++ 代码 概述 当无法用 Java 语言编写整个应用程序时,JNI 允许您使用本机代码。在下列典型情况下,您可能决定使用本机代码: 希望用更低级、更快的编程语言去实现对时间...

Jerikc
2012/10/08
0
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 进行 Java 编程(1)

本教程是关于什么的? Java 本机接口(Java Native Interface (JNI))是一个本机编程接口,它是 Java 软件开发工具箱(Java Software Development Kit (SDK))的一部分。JNI 允许 Java 代码使...

Jerikc
2012/10/08
0
0
Android JNI(一)——NDK与JNI基础

本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Native相互调用 Android JNI学习(四)——JNI的常用方法...

隔壁老李头
05/09
0
0
用 JNI 进行 Java 编程(5)

结束语和参考资料 结束语 Java 本机接口是 Java 平台中一种设计良好和良好集成的 API。它被设计成用来使您能将本机代码合并到 Java 程序中,也为您提供了一种在用其它编程语言编写的程序中使...

Jerikc
2012/10/08
0
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和C/C++交互

1、Java调用C/C++: Java代码 [JNITest.java]: package darcy; public class JNITest { static{ System.loadLibrary("Hello"); } public native void HelloKitty(); public static void main(......

叶大侠
2014/03/18
0
0
android开发教程(4)— jni编程之采用 javah 从java调用C++

用Java调用C/C++代码 当无法用 Java 语言编写整个应用程序时,JNI 允许您使用本机代码。在下列典型情况下,您可能决定使用本机代码: 希望用更低级、更快的编程语言去实现对时间有严格要求的...

刘小米
2014/09/18
0
1
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

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Tomcat内存设置

第一步骤: 1、路径是Tomcat目录下的/bin/catalina.bat文件 打开catalina.bat文件 加入下面这句代码: set JAVA_OPTS=%JAVA_OPTS% -server -XX:PermSize=128M -XX:MaxPermSize=512m 2、路径是...

一梦心草
7分钟前
0
0
IDEA 及 Gradle 使用总结

IDEA 及 Gradle 使用总结 自动编译组件 目前Android开发的主流开发工具是 Eclipse 和 IDEA 目前主流的自动化打包工具时 ant,maven,gradle。 maven工具中有自己的依赖仓库维护,很多开源支持...

舒文joven
7分钟前
0
0
lombok 引入后,测试类始终找不到get,set方法。

开发环境为idea,jdk1.7,maven3.5. 网上直接搜出来的方法有: 1、在setting里安装lombok的plugins; 2、如下图,勾选enable annocation processing选项 3、升级maven plugins插件 我尝试了以...

Kidult
9分钟前
0
0
Duang,HUAWEI DevEco IDE全面升级啦

想感受全新UI带来的视觉及交互体验、 HiKey970开发板调测、 HiAI API推荐和收藏、 深度AI模型分析等新功能, 体验高清晰度和流畅度的远程AI真机调测吗? 全新的UI设计 采用最优秀的视觉及交互...

华为终端开放实验室
17分钟前
0
0
阻止事件冒泡,阻止默认事件

1.event.stopPropagation()方法 这是阻止事件的冒泡方法,不让事件向documen上蔓延,但是默认事件任然会执行,当你掉用这个方法的时候,如果点击一个连接,这个连接仍然会被打开, 2.event....

闫亚亚
19分钟前
0
0
网络监控工具类

package com.guorentong.learn.organ.utils;import android.app.Activity;import android.content.Context;import android.content.Intent;import android.net.ConnectivityManage......

王先森oO
20分钟前
0
0
命令

sudo nginx -s reload 重启nginx sudo lsof -i -P | grep -i "listen" 查看端口占用

那个猴子
23分钟前
0
0
用scrapy-redis爬去新浪-以及把数据存储到

需求:爬取新浪网导航页(http://news.sina.com.cn/guide/)所有下所有大类、小类、小类里的子链接,以及子链接页面的新闻内容。 准备工作: a.安装redis(windows或者linux) b.安装Redis Des...

丁典
24分钟前
0
0
PHP常用函数篇

1.为什么要使用函数? 除了内建的PHP函数,我们可以创建我们自己的函数。 函数是可以在程序中重复使用的语句块。 使代码逻辑更清晰 避免过多的全局变量 封装后避免相同逻辑重复代码,只需调用...

天地有涯风有信_大海无量不见人
25分钟前
0
0
对List分组

在日常工作中会遇到这样的情景,我们需要对List按照List中对象的一个值进行分组。比如一个Human的List,我们要根据性别分组,传统的方法是做双层循环,逐个对比,今天我要介绍一种详单简单的...

珂jack
26分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部