在上一个随笔中介绍了怎样实现一个简单的Jni小程序。在这一篇里主要是说一下JAVA与C++之间的参数传递问题。
本人是个Java程序员,工作没几年 菜鸟级别,C++不是很熟悉,但对.NET到是了解一些,所以这里面的C++部分都用C++.net来讲的。为了便于理解,文档中可能会有很多通俗易懂的白话,最近也是项目中用到了Jni 才学习了几天,所以这里要是有哪说得不对,还请大家见谅,发现问题就指出来,大家一起学习 哈!
先大致回顾下上一篇的内容,在上一篇里我们创建了一个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<: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 void JNICALL 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中的方法就不过多的说了,自己看就应该能会用。在以后的随笔里会多说一些数据转换的问题。