在上一个随笔中介绍了怎样实现一个简单的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<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 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中的方法就不过多的说了,自己看就应该能会用。在以后的随笔里会多说一些数据转换的问题。

 

  

  

 

  

  

  

posted on 2012-01-09 22:50  憤怒的小鳥  阅读(39794)  评论(0编辑  收藏  举报