东芝复印机181,可乐云张飞跃,伯克希尔哈撒韦公司
jni是java native interface的简称,通过jni,可以调用c++或c的程序(称为本地程序)。
我认为应用场景可以用三个字概括“不得不”,即只有遇到必须跨语言的时候,才会选择jni。从java的场景出发,使用jni意味着失去了跨平台的优势;从c++的角度想,可能需要将程序发布到安卓端等,而不得不采用jni进行跨语言。
通过调研jni,目前jni的应用场景大部分是需要在安卓平台引入c/c++代码。也有人和我一样,正在开发的java系统需要调用一段c++的核心代码。于是,让我们愉快地入坑吧~
本文主要讲解在idea2018和vs2017平台下搭建一套java项目和c++解决方案,其中java项目实现一个简单的helloworld功能,该功能正是通过jni调用c++实现的。
作为一名jni刚入坑小将,以及多年没写过(只在书本和考试中与其交手)c++的渣渣,写一个helloworld程序也是反复磨炼的过程。在反复磨炼过程中,我发现网上很多教程有些繁琐,需要将各种文件复制来粘贴去,稍有错误就要重新复制粘贴,实在让人恼火。所以本文介绍一种借助idea和vs平台尽量减少步骤的搭建方法。
vs:2017
tips:
windows系统注定了生成的动态链接库是dll文件
jdk10将javah工具取消,需要使用javac -h替代,这是与jdk8不同的地方
首先本次项目主要想实现一个简单的helloworld,java程序声明sayhello函数,并将name当做参数传入。在c++中实现sayhello,将sayhello的文本传回给java程序。步骤如下
在包下创建一个类,用来编写native方法和main函数。
package com.study.jni.demo.simple; import com.study.jni.demo.common.constants; public class simplehello { public static native string sayhello(string name); public static void main(string[] args) { string name = "lucychen"; string text = sayhello(name); system.out.println("after native, java shows:" + text); } static { // system.loadlibrary("jnicppdemo"); system.load(constants.dllpath + "jnicppdemo.dll"); } }
其中sayhello是一个静态方法,在前面标注为native代表了这是一个本地函数。
main函数中,调用sayhello函数。
下面的static代码块暂且不谈。
代码写好后,在生成头文件前,我们需要build一下项目,生成class文件,build后,可在左侧目录看到out/production目录下生成了对应class文件。
头文件可以使用命令行生成(见),或者熟悉格式后自己手写。但是正如前文的介绍,本文希望用一种简便的方式。所以我希望能够随便点一下就生成头文件(真的有点懒得)。于是,我找到了一种,那就是external tools。
external tools其实就是将手动输入的命令存下来,本质也是运行javah,后面跟着配置参数,这些参数存在external tools,避免每次手动输入。
添加external tools.file->settings->tools->externaltools,点击添加
编辑tools
name:generate header file program:$jdkpath$/bin/javah arguments:-jni -classpath $outputpath$ -d ./jni $fileclass$ working directory: $projectfiledir$
然后你就会发现我们的目录中多了一个jni文件夹,jni文件夹里面有一个名字长长的.h文件,成功!
tips:
该方法适用于jdk8,jdk10中取消了javah,适用javac -h。但是jdk10在使用external tools时会报错。但是我的工作环境不可能用jdk10,所以我也没有钻进去研究了~
长时间没接触过c++了,想当年(10年前)上学那会,我还只会用vc6.0刷刷题,而现在都要vs2017了,而我的c++知识早就忘得差不多了。虽然我作为一个小白,但是仍然阻挡不了我吐槽vs的中文翻译——解决方案,解决方,解决,解,角……emm,真变扭。废话不多说了,我们一起创建一个"解决方案"吧!
文件->新建->项目->windows桌面->windows桌面向导,输入名称。
选择应用程序类型,注意此处不要勾选预编译标头【】
点击项目,我的项目叫jnicppdemo,在菜单栏选择项目->属性->配置属性->vc++目录->包含目录
创建一个cpp文件,其中include jni.h,刚刚生成的头文件。如果上一步设置路径成功,这里不会报错。
在这个cpp,参考了,实现sayhello,即获取参数name,并返回hello name给java程序。
#include "jni.h" #include "stdio.h" #include "string.h" #include "com_study_jni_demo_simple_simplehello.h" jniexport jstring jnicall java_com_study_jni_demo_simple_simplehello_sayhello( jnienv *env, jclass cls, jstring j_str) { const char *c_str = null; char buff[128] = { 0 }; jboolean iscopy; c_str = env->getstringutfchars(j_str, &iscopy); if (c_str == null) { printf("out of memory.\n"); return null; } printf("java str:%x %s %d %d\n", c_str, c_str, strlen(c_str), iscopy); sprintf_s(buff, "hello %s", c_str); env->releasestringutfchars(j_str, c_str); return env->newstringutf(buff); }
tips
c++的调用方式和c的调用jni的方式不同,在jni.h中可以看出来,网上很多教程都是基于c的,我这里将其改成了c++的调用方式
写好了cpp,让我们勇敢地生成dll文件吧。
参考文献里指出需要将解决方案平台改成64bit,那就改一下吧,毕竟我们需要运行在64位操作系统上。
然后右击项目生成/重新生成,就生成了dll文件。从控制台输出可看到dll的地址
生成dll文件后,让我们重新回到java项目,我们继续来讨论刚刚遗留的一段代码
static { // system.loadlibrary("jnicppdemo"); system.load(constants.dllpath + "jnicppdemo.dll"); }
含义很好理解,就是在java里面加载dll库。注释的方法是如果把dll库拷贝到java项目路径下,可以采用这种方式加载,不用写路径,不需要写后缀。
还有一种采用systerm.load方式,这种需要指定库的位置并加上后缀。由于我在学习过程中,可能会遇到各种问题,来回修改和拷贝很麻烦,于是我采用了第二种方式,为了代码规范及好看,我新建了一个类存放dll的地址。
至此,一个简单的helloworld的jni项目搭建就打通了,本文采用idea2018和vs2017ide环境,实现了一种简便的搭建方法,这种方法可方便地应用在jni学习过程中。
后面会更新到github中,敬请期待
如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复
浅析我对 String、StringBuilder、StringBuffer 的理解
使用IDEA搭建SSM框架的详细教程(spring + springMVC +MyBatis)
Springboot整合freemarker 404问题解决方案
引入mybatis-plus报 Invalid bound statement错误问题的解决方法
网友评论