在java中使用和创建自定义的native方法
来源:互联网 发布:巴西经济危机 知乎 编辑:程序博客网 时间:2024/04/30 05:35
JNI:Java Native Interface(Java本地接口)
使用了JNI接口的JAVA程序,不再像以前那样自由的跨平台。因为JNI底层是用C/C++实现的,后者是依赖操作平台的,要实现跨平台,就必须将本地代码在不同的操作系统平台下编译出相应的动态库。下面简单介绍下JNI的开发流程。
从编写到运行得到结果共需要执行6步:
(1)编写Java源代码、(2)将Java源代码编译成class字节码文件、(3)用javah命令生成.h头文件、(4)用本地代码实现.h头文件中的函数、(5)将本地代码编译成动态库、(6)运行Java程序
1、编写Java源代码
package com.evan;public class Hello{ public static void main(String[] args){ System.out.println("---------basic type test---------"); System.out.println(""+5+"+"+8+"="+sum(5,8)); System.out.println("---------String type test---------"); if(args.length == 0){ System.out.println("input the data"); return; } String newString = addHeaderString(args[0]); System.out.println(newString); System.out.println("length="+newString.length()); System.out.println("---------Array type test---------"); int[] data = new int[10]; System.out.println("primitive data is:"); for(int ii=0;ii<data.length;ii++){ data[ii] = ii; System.out.print(data[ii]+" "); } System.out.println(); System.out.println("improved data is:"); int[] result = improve(data,data.length); for(int ii=0;ii<result.length;ii++){ System.out.print(result[ii]+","); } System.out.println(); System.out.println("primitive data is:"); for(int ii=0;ii<data.length;ii++){ data[ii] = ii; System.out.print(data[ii]+" "); } } private static native String addHeaderString(String str); private static native int sum(int a,int b); private static native int[] improve(int[] data,int size); static{ System.loadLibrary("Hello"); /*when classload load the class input jvm,it will load the methods of Hello.dll into native method of jvm; this way suppport the input with absolute path.such as xx/xx/Hello.dll; */ }}
2、将Java源代码编译成class字节码文件
操作:
javac Hello.java -d ./
备注:
-d指明了产生的Hello.class文件放置的位置;
结果:
得到./com/evan/Hello.class
3、用javah命令生成.h头文件
操作:
javah -classpath ./ -d ./ com.evan.Hello
备注:
-classpath 指定class目标文件所在目录, 程序将会从指定的目录去寻找目标类,即最后的com.evan.Hello
结果:
得到com_evan_Hello.h文件
#include <jni.h>/* Header for class com_evan_Hello */#ifndef _Included_com_evan_Hello#define _Included_com_evan_Hello#ifdef __cplusplusextern "C" {#endif/* * Class: com_evan_Hello * Method: addHeaderString * Signature: (Ljava/lang/String;)Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_com_evan_Hello_addHeaderString (JNIEnv *, jclass, jstring);/* * Class: com_evan_Hello * Method: sum * Signature: (II)I */JNIEXPORT jint JNICALL Java_com_evan_Hello_sum (JNIEnv *, jclass, jint, jint);/* * Class: com_evan_Hello * Method: improve * Signature: ([II)[I */JNIEXPORT jintArray JNICALL Java_com_evan_Hello_improve (JNIEnv *, jclass, jintArray, jint);#ifdef __cplusplus}#endif#endif
4、用本地代码实现.h头文件中的函数
操作:
编写一个c或者c++文件,这里创建了一个Hello.c文件;
#include "com_evan_Hello.h"#include <String.h>JNIEXPORT jstring JNICALL Java_com_evan_Hello_addHeaderString(JNIEnv *env, jclass jcl, jstring jstr){ const char *cstr = (*env)->GetStringUTFChars(env,jstr,0); //获取参数jstr的指针;之后通过cstr就可以访问jstr中的数据;最后一个参数是isCopy? jsize size = (*env)->GetStringUTFLength(env,jstr); //获取String数据的长度 char buff[256] = {0}; //注意C语言必须要将所有的类型声明放在函数最开始的位置! if(cstr==NULL){ printf("out of memory \n"); } (*env)->ReleaseStringUTFChars(env,jstr,cstr); //释放本函数生成的指向jstr的指针,类似于将cstr置空 //该方法与GetStringUTFChars方法成对出现 sprintf(buff,"evan %s %d",cstr ,size); //这是一个C方法,用于打印字符串到有一个字符数组当中 return (*env)->NewStringUTF(env,buff); //这里是创建一个新的jstring对象,新对象的值由buff决定,长度是buff实际的值;即一般是小于256 }JNIEXPORT jint JNICALL Java_com_evan_Hello_sum (JNIEnv * env, jclass jcl, jint a, jint b){ return a+b;}JNIEXPORT jintArray JNICALL Java_com_evan_Hello_improve (JNIEnv *env, jclass jcl, jintArray array,jint size){ jint* intarray = (*env)->GetIntArrayElements(env,array,0); //本地获取到一个访问java堆中数组的指针 jintArray data = (*env)->NewIntArray(env,size); //生成一个长度为size的IntArray数组 int index = 0; jint buff[100]; int length=0; if(size<100) length=size; else length =100; if(data==NULL){ printf("out of memory \n"); } for(;index<length;index++){ intarray[index]++; //这里虽然改了这个指针所指向的数据, //但是在java堆中所存储的数组并不会改变,即java类中定义的数组并不会发生变化 buff[index] = 2+intarray[index]; } (*env)->ReleaseIntArrayElements(env,array,intarray,0); //释放intarray指针 (*env)->SetIntArrayRegion(env,data, 0,length,buff); //将buff的数据复制到data中 return data;}
5、将本地代码编译成动态库
关于动态库的说明:
即将上面的Hello.c变成Hello.dll等动态库文件;
windows对应动态库为*.dll ,linux/unix对应动态库为 *.so ,mac os x对应动态库为 *.jnilib
下面以windows环境来说明如何实现xx.c-->xx.dll文件的转变
操作:
cl -I %JAVA_HOME%\include -I %JAVA_HOME%\include\win32 -LD Hello.c -FeHello.dll
备注:
在cmd中输入是没有该命令的,推荐进入《VS2012 X64工具命令提示》输入cl是有该命令的;
-I指定包含编译的头文件,所在的目录
-LD 指定被编译动态库的目标文件
-Fe 指定生成的动态链接库的文件名;千万注意Fe和后面的名字不能有空格!!!
结果:
得到文件Hello,dll6、运行Java程序
注意:运行java程序前需要确保java程序能够找得到对应的dll文件
法一:
java -Djava.library.path=./ com.evan.Hello
备注:
-D设置环境变量java.library.path的值,因为我们上面生成的Hello.dll放在当前目录,所以我们就指定了java.library.path=./
法二:
将Hello.dll放入你的jdk安装包的bin目录下;
然后执行java com.evan.Hello;
补充:
你可以运行一下System.out.println(System.getProperty("java.library.path"));
然后从结果中选择任何一个目录放入你的Hello.dll文件,也能达到上面同样的效果;
执行结果如下:
---------basic type test---------5+8=13---------String type test---------evan hjk 3length=10---------Array type test---------primitive data is:0 1 2 3 4 5 6 7 8 9improved data is:3,4,5,6,7,8,9,10,11,12,primitive data is:0 1 2 3 4 5 6 7 8 9
补充:
native实现方法中常用的几种数据处理
- 基本数据
- 与平时的操作没有太大的区别,只是在类型前加上一个j符号,如int变成jint; boolean变成jboolean
- String类型数据
- 以函数参数(JNIEnv *env, jobject obj, jstring string)为例
- 获得jstring的指针:const char* str = (*env)->GetStringUTFChars(env,string,0);
- 释放上面得到的指针(减少引用): (*env)->ReleaseStringUTFChars(env,string,str);
- 获得jstring的长度:jsize size = (*env)->GetStringUTFLength(env,string);
- 创建一个jstring数据(一般是作为一个jstring返回值时才用):(*env)->NewStringUTF(env,string,buff);
- 上面的buff是我们在函数内部定义的形如 char buff[256]的一个对象;
- 上面得到的jstring长度等于buff存储的数据实际长度,不等于buff定义的长度(256);
- 注意:return (*env)->NewStringUTF(env,string,buff); 不报错;jstring mstr = (*env)->NewStringUTF(env,string,buff)报错;
- 数组 (下面以int数组为例,其他数组直接将int换成对应的关键字即可)
- 以函数参数(JNIEnv *env, jobject obj, jintArray array)为例
- 获得jintArray的指针:jint* data = (*env)->GetIntArrayElements(env,array,0);
- 释放上面得到的指针(减少引用): (*env)->ReleaseIntArrayElements(env,array,data);
- 获得jintArray的长度:jsize size = (*env)->GetIntArrayLength(env,array);
- 创建一个jintArray数据(一般是作为一个jintArray返回值时才用):jintArray data = (*env)->NewIntArray(env,100); (与string数据不同之处)
- 这里开辟了一块大小为100的jintArray数组区间;
- 对上面开辟的数据赋值:(*env)->SetIntArrayRegion(env, data,0,100,buff); (与string数据不同之处)
- 上面的buff是我们在函数内部定义的形如 jint buff[256]的一个对象;
- 括号中的0 100是限制data的 ,buff是从0开始
- 上面语句的含义就是将buff 0-99的数据复制到 data的0-99位置
- 对于其他类型如自定义object等,参考链接:http://hubingforever.blog.163.com/blog/static/17104057920115992032177/
更多方法的参数和返回值参考:XX/jdk/include/jni.h;xx为的java jdk安装目录;
1 0
- 在java中使用和创建自定义的native方法
- JAVA中native方法的使用
- java中native方法的使用
- 在java中调用c程序--native方法的学习
- native应用 在C中调用JAVA的方法
- Java的native方法使用
- 一个使用c++在lua中创建自定义数据类型的简易方法
- 一个使用c++在lua中创建自定义数据类型的简易方法
- Java创建自定义标签SimpleTagSupport并在页面中使用
- Android JNI 在C中调用Java(包括自定义的Java方法和Log)
- 3.React Native在Android中自定义Component和Module
- 在Android Native层中创建Java虚拟机实例
- JAVA中native方法
- java中native方法
- Java中Native方法
- Java中native方法
- Java中native方法
- JAVA中native方法
- POJ 3648 Wedding 2-SAT
- 梯度下降(Gradient Descent),一句代码,一个式子
- ViewDragHelper 实现 ListView 左滑删除
- 十六、初学jsp之jstl标签与自定义标签
- android调用第三方api实现用户数授权登录机制详解
- 在java中使用和创建自定义的native方法
- C++ Exceptional 有关const的用法
- json基本用法(1)
- 十七、初学jsp之jsp过滤器
- hdu1249三角形
- 单选按钮组中获取选定的选项并传递到另一个Activity
- ZOJ 3327Friend Number(模拟题)
- 十八、初学jsp之八大监听器
- iOS 8 blurEffect模糊效果