我们创建了一个JAVA类Test.java和C++.net的Jin01项目。主要是实现Jin01中输出“第一个Jni小程序”,然后用Test.java来调用。
Jni01中的函数是:
#include "stdafx.h"
#include "WINSCARD.H"
#include "com_test01_Test.h"
using namespace System;
int main(array<System::String ^> ^args) {
Console::WriteLine(L"Hello World");
return 0;
}
//实现Java_com_test01_Test_firstTest方法
JNIEXPORT void JNICALL Java_com_test01_Test_firstTest(JNIEnv *, jobject){
Console::WriteLine(L"第一个Jni小程序");
}
Test.java代码如下:
public class Test {
public native void firstTest();//
public static void main(String[] args) {
System.loadLibrary("Jni01");
Test t=new Test();
t.firstTest();
}
}
1、com_test01_Test.h头文件中实现Jni接口方法深入研究
在上一篇的例子中实现Jni接口的方法声明在com_test01_Test.h头文件中,代码如下:(以下按照字体颜色来解释每个部分的意思)
JNIEXPORT voidJNICALL Java_com_test01_Test_firstTest (JNIEnv *env, jobject obj);
(1)JNIEXPORT :在Jni编程中所有本地语言实现Jni接口的方法前面都有一个"JNIEXPORT",这个可以看做是Jni的一个标志,至今为止没发现它有什么特殊的用处。
(2)void :这个学过编程的人都知道,当然是方法的返回值了。
(3)JNICALL :这个可以理解为Jni 和Call两个部分,和起来的意思就是 Jni调用XXX(后面的XXX就是JAVA的方法名)。
(4)Java_com_test01_Test_firstTest:这个就是被上一步中被调用的部分,也就是Java中的native 方法名,这里起名字的方式比较特别,是:包名+类名+方法名。
(5)JNIEnv * env:这个env可以看做是Jni接口本身的一个对象,在上一篇中提到的jni.h头文件中存在着大量被封装好的函数,这些函数也是Jni编程中经常被使用到的,要想调用这些函数就需要使用JNIEnv这个对象。例如:env->GetObjectClass()。(详情请查看jni.h)
(6)jobject obj:刚才在Test类的main方法中有这样一段代码:
Test t=new Test(); t.firstTest();
这个jobject需要两种情况分析。上段代码中firstTest方法是一个非静态方法,在Java中要想调用它必须先实例化对象,然后再用对象调用它,那这个时候jobject就可以看做Java类的一个实例化对象,也就是obj就是t。如果firstTest是一个静态方法,那么在Java中,它不是属于一个对象的,而是属于一个类的,Java中用Test.firstTest()这样的方式来调用,这个时候jobject就可以看做是java类的本身,也就是obj就是Test.class。
2、Jni中的数据类型
每一个Java的数据类型在Jni中都一个和它相对应的数据库类型,这样才能保证Java调用C或者C++的过程中数据的正确性。
打开Jni.h文件,有如下代码:
这里声明了所有Jni支持的数据类型,可以发现一个规律所有Jni的数据类型前面都有一个”J“字母,这样主要是为了好记。在Java中所有的对象都以引用的形式体现的,为了保持一致 所以在C与C++中使用了指针类型。Java与Jni中数据类型的对照表如下:
3、实例:在C++.net程序中改变Java中变量的值
在原有Test.java中声明一个整形变量message,如下:
public class Test {
public native void firstTest();//第一个Jni
public int message;
public static void main(String[] args) {
System.loadLibrary("Jni01");
new Test().firstTest();
}
}
在Jni01的 Java_com_test01_Test_firstTest方法中写如下代码:
JNIEXPORT void JNICALL Java_com_test01_Test_firstTest(JNIEnv * env, jobject obj){
jclass class_Test=env->GetObjectClass(obj); //注释(1)
jfieldID fid_msg=env->GetFieldID(class_Test,"message","I");//注释(2)
env->SetIntField(obj,fid_msg,123);//注释(3)
}
(1)调用GetObjectClass方法来获取Jclass,GetObjectClass的参数就是obj
(2)调用GetFieldID方法来获取jfieldID,这里要说明一下Jni的所有操作,其实就是操作方法或者是操作属性两种。操作方法时需要根据方法的ID(jmethodID)来操作,可以理解为jmethodID标识了这个方法,也就是通过这个jmethodID可以找到你要找的方法。同理操作属性时也要根据该属性的ID(jfieldID )来操作。上面那段代码里我们要改变 变量message的值,所以要先获取该变量的jfieldID 。获取变量的jfieldID 方法是GetFieldID。GetFieldID需要3个参数。第一个是上一步获取的Jclass,第二个参数是Java中的变量名,最后一个参数是变量签名(int 的变量签名是”I“)。
下面是所有Jni中的变量签名列表:
(3)最后调用SetIntField方法就可以设置变量 message的值了。在JAVA的测试环境中打印一下变量的值就知道是否成功了。
全部完成后,可以自己试验下使用GetIntField方法将message的值取出来。对于这些Jni.h中的方法就不过多的说了,自己看就应该能会用。在以后的随笔里会多说一些数据转换的问题。
具体实例见:http://blog.youkuaiyun.com/zengrun1992/article/details/40076171
转自http://www.cnblogs.com/lsnproj/archive/2012/01/09/2317519.html