JNI的初步了解与简单示例

来源:互联网 发布:美不美看大腿软件 编辑:程序博客网 时间:2024/05/29 02:16

说实在的,我只是知道jni的意思,但是真正运用到jni的事例却是没有过,所以对jni真的是没怎么去了解过,但是现在看来,学习jni真的是很有必要,在很多方面可能对你会很有帮助,或者对你技术的提升会有很大的帮助。
首先我们来先了解jni到底是什么?它有什么作用?我们为什么要学习它,什么情况需要用到jni?然后我们在通过几个demo来学习一下。
Jni的全称是java native interface(java的本地接口),说白了就是java调用的本地的c、c++的接口,所以我们可以猜到jni就是java调用本地的c/c++方法来完成自身程序功能的一个工具。我们设想一下啊,如果你的Java代码,需要得到一个文件的属性,但是你找遍了JDK帮助文档也找不到相关的API;如果本地有一个其他的项目,不过不是Java语言实现的,而是其他语言实现的,这个时候你的老板要求你把两套系统整合到一起,你会怎么整合呢?
如果你的Java代码中需要用到某种算法,不过算法是用C实现并封装在动态链接库文件(DLL)当中的,你要怎么去调用呢?还有就是我之前说多的物联,底层的硬件是有c语言控制的,你的app要实现硬件的一些操作,你会怎么去调用呢?而且,用jni也大大增加了程序的安全性,反编译的出来的概率大大减小。
当然,jni也是有一些缺点的,就是不是很智能,写类,方法,都要自己去jni文件中找,也没有提示,大大降低了编码的效率。
Jni很好的帮我们解决了这一些问题。
下面我们通过几个demo来学习一下如何使用jni:
1.简单的helloworld
似乎所有的程序员都是从helloworld开始的,ok,这些demo我都是在虚拟机完成的,不过不用虚拟机也可以,先不用ide来写我们的demo,直接用记事本写就可以了,这主要是为了方便直观,先写一个java类:
public class Jason
{
//native修饰符:此方法不是抽象方法,它的实现体在本地语言中
public native void printHelloworld(String str);

/*
public void printHelloworld()
{
System.out.println(“hello world”);
}
*/

public static void main(String[]args){    PrintHelloWorld t=new PrintHelloWorld();    t.printHelloworld("weclome to gz");}//静态加载动态库libjasonhello.sostatic{    System.loadLibrary("jasonhello");}

}

接着编译一下java类,在用javah编译一下java类生成头文件(c类的),就是Jason.h文件,在根据.h文件编写c类:
/* DO NOT EDIT THIS FILE - it is machine generated */

include “PrintHelloWorld.h”

include “stdio.h”

JNIEXPORT void JNICALL Java_Jason_printHelloworld
(JNIEnv *env, jobject obj, jstring str)
{

const char* cstr=(*env)->GetStringUTFChars(env,str,0);    //const char* (JNICALL *GetStringUTFChars)  //(JNIEnv *env, jstring str, jboolean *isCopy);printf("%s \n",cstr);

}
其中那些如GetStringUTFChars方法可以在jdk中jni文件中查询,这个方法是将string类转换成char类,之所以转换是因为c语言输出的都是char类,没有string,写完之后要生成.so文件,就是动态库了,我都是放在同一个文件夹的,用的是cmd编译,所以要输入一下命令行
这里写图片描述
这里写图片描述

其中的gccxxxx xxx.so xxx.c 就是根据c文件来生成动态库,还要把动态库考在java.library.path这个路径,不然要报错,至于这个路径怎么找,可以写一下代码:
public class GetJavaLibraryPath
{
public static void main(String[]args)
{
String path=System.getProperty(“java.library.path”);

    System.out.println(path);}

}
返回的就是java.library.path路径,考到其中的一条路径就好了,cp -a xxx.so usr/lib 就是将动态库考到usr中lib文件里面,接着直接运行java就可以了。

2 从c库中获取参数的demo

先编写java类:
public class PrintHelloWorld
{
//native修饰符:此方法不是抽象方法,它的实现体在本地语言中
public native void printHelloworld(String str);
public native String gecCStr();
public static void main(String[]args)
{
PrintHelloWorld t=new PrintHelloWorld();
String str=t.gecCStr();
System.out.println(str);
}

//静态加载动态库libgechelloworld78_03.sostatic{    System.loadLibrary("gechelloworld78_03");}

}

接着编译生产class和.h,在根据.h来编写c文件,步骤跟1差不多,

include “PrintHelloWorld.h”

JNIEXPORT void JNICALL Java_PrintHelloWorld_printHelloworld
(JNIEnv *env, jobject obj, jstring str)
{

}

JNIEXPORT jstring JNICALL Java_PrintHelloWorld_gecCStr
(JNIEnv *env, jobject obj)
{

const char *cstr="c hello world";//jstring (JNICALL *NewStringUTF)  //(JNIEnv *env, const char *utf);jstring returnString=(*env)->NewStringUTF(env,cstr);return returnString;

}

这些方法就是在jni文件中找的,//jstring (JNICALL *NewStringUTF)(JNIEnv *env, const char *utf);,这个const char *utf就是我们要返回的java的参数值,这个方法是将他转成字符串。接下来步骤跟1一样,生产.so跟拷贝,这里就不累赘了。

3在c库中调用java类中的方法demo
public class PrintHelloWorld
{

int a=5;//native修饰符:此方法不是抽象方法,它的实现体在本地语言中public native void printHelloworld(String str);public native String gecCStr();public native void accessField();public native void setFieldValue();public void callBackListener(int c){    System.out.println("callBackListener c="+c);}public static void main(String[]args){    PrintHelloWorld t=new PrintHelloWorld();    t.accessField();    t.setFieldValue();    System.out.println(t.a);}//静态加载动态库libgechelloworld78_04.sostatic{    System.loadLibrary("gechelloworld78_04");}

}
Java代码中我们可以看到main函数中是没有调用callBackListener方法,就是没有任何输出方法,接下来一样生成编写c文件:

include “PrintHelloWorld.h”

JNIEXPORT void JNICALL Java_PrintHelloWorld_printHelloworld
(JNIEnv *env, jobject obj, jstring str)
{

}

JNIEXPORT jstring JNICALL Java_PrintHelloWorld_gecCStr
(JNIEnv *env, jobject obj)
{
return (void*)0;
}

JNIEXPORT void JNICALL Java_PrintHelloWorld_accessField
(JNIEnv *env, jobject obj)
{

        //jclass (JNICALL *GetObjectClass)    //              (JNIEnv *env, jobject obj);    jclass cls=(*env)->GetObjectClass(env,obj);    //jfieldID (JNICALL *GetFieldID)                //(JNIEnv *env, jclass clazz, const char *name, const char *sig);    jfieldID  fieldId=(*env)->GetFieldID(env,cls,"a","I");    //jint (JNICALL *GetIntField)          //    (JNIEnv *env, jobject obj, jfieldID fieldID);          jint aValue=(*env)->GetIntField(env,obj,fieldId);          printf("a=%d \n",aValue);

}

JNIEXPORT void JNICALL Java_PrintHelloWorld_setFieldValue
(JNIEnv *env, jobject obj)
{

    jclass cls=(*env)->GetObjectClass(env,obj);    //jfieldID (JNICALL *GetFieldID)                //(JNIEnv *env, jclass clazz, const char *name, const char *sig);    jfieldID  fieldId=(*env)->GetFieldID(env,cls,"a","I");    (*env)->SetIntField(env,obj,fieldId,10);    //void (JNICALL *SetIntField)  //(JNIEnv *env, jobject obj, jfieldID fieldID, jint val);  //    jmethodID (JNICALL *GetMethodID)  //(JNIEnv *env, jclass clazz, const char *name, const char *sig);  jmethodID mId=(*env)->GetMethodID(env,cls,"callBackListener","(I)V");      //void (JNICALL *CallVoidMethod)  //(JNIEnv *env, jobject obj, jmethodID methodID, ...);  (*env)->CallVoidMethod(env,obj,mId,4);

}
这个前两个demo一样,都是要从jni中找对应的方法,其中const char *sig这个指的是变量或者方法在java中的签名,可以用javap -s -p xxx(java类)来查看签名。

至于,jni在eclipse中如何使用,这个我会在后面见到,这里还涉及到一个叫NDK的东西,是帮助我们在eclipse中编写jni的。

0 0
原创粉丝点击