有时候我们的C++/C带代码是现成的,需要向上用JNI封装,然后用Java调用。Java中是没有指针的,但是Java通过JNI调用C++/C接口,C++/C是有指针的,这种情况Java该作何处理。

  1. C++/C指针作为输出参数

  这种在前面出参为String类型的情况最后更为一般的情况已经提到过。这里在展开来讨论。

  • 基本类型

     example:

     Java接口:

     public void getAge(int age[]);

     JNI:

     JNIEXPORT void JNICALL Java_setAge_ 1native (JNIEnv *env, jclass thiz,

                                                  jintArray age)

    {      

        int age;

        getAge(&age); // setAge为更下层的函数接口

        jint  as32AgeArray[1];

        s32AgeArray [0] = age;

       

        /* 这个操作实际有些像memcpy,如果数组大小比较大,并且拷贝若干

            个连续的元素,就修改上面方法的第2,3个参数即可

        */

       env->SetIntArrayRegion(age, 0, 1, (jint *) as32AgeArray);

                                    }

  • 输出参数类型是class

    /* C++/C中 struct */

  typedef struct _Para_tag

  {

       int x;

       short y;

       char az[100];

       char h;

       unsigned int t;

   }Para_Tag;


   对应的Java中的class为:

   public class Para_Tag

   {

       int m_x;

       short m_y;

       String m_z;

       char m_h;

       long m_t;

       public Para_tag(int x,short y,String z,char h,long t)

      {

           m_x = x;

           m_y = y;

           m_z = z;

           m_h = h;

           m_t = t;

       }

   }

   

  Java 接口:

  public  void getPara(Para_Tag tag);


   JNI:


  JNIEXPORT void  JNICALL Java_getPara_ 1native (JNIEnv *env, jclass thiz, jobject obj)

  {

       Para_Tag  tag;


       getPara(&tag);// 取得C++/C中Para_Tag结构体的值

       // 给java的Para_Tag class赋值

       jclass DataCls = env->FindClass("com/Para_Tag"); // 通过class path找到class的标识


       /*  对class中的每个变量逐一赋值 */

       jfieldID dataId = env->GetFieldID(DataCls, "m_x", "I");  

       env->SetIntField(obj, dataId,tag.x);

       dataId = env->GetFieldID(DataCls, "m_y", "S");

       env->SetShortField(obj, dataId,tag.y);

       /*  注意C++中的char * 类型如何通过JNI转换为Java的String  */

       dataId = env->GetFieldID(DataCls, "m_z", "Ljava/lang/String;");        

       jstring strTmp = env->NewStringUTF((char *)tag.az);

       env->SetObjectField(objdata, dataId, strTmp);

       dataId = env->GetFieldID(DataCls, "m_h", "C");

       env->SetCharField(obj, dataId,tag.h);

       dataId = env->GetFieldID(DataCls, "m_t", "J"); // 注意long对应的签名是J,不是L

       env->SetLongField(obj, dataId,tag.t);

   }

   上面这种做法是ok的,但是效率太低,每调用一次vmenv函数就会和虚拟机交互一次,,和虚拟机交互次数太多,有没有什么办法提高效率。

   有,那就是如果对class中的多个成员变量都要赋值的话可以调用java的构造函数赋值,这样不管有多少个函数,只会和虚拟机交互一次,会大大提高效率。

   修改后用构造函数赋值的例子如下:

        JNIEXPORT void  JNICALL Java_getPara_ 1native (JNIEnv *env, jclass thiz, jobject obj)

       {

            Para_Tag  tag;

     getPara(&tag);// 取得C++/CPara_Tag结构体的值

     jclass DataCls = env->FindClass("com/Para_Tag"); // 通过class path找到class的标

           jmethodID RtnInitId = env->GetMethodID(DataCls, "<init>", "(ISLjava/lang/String;CJ)V");

           jstring strTmp = env->NewStringUTF((char *)tag.az);

           env->CallVoidMethod(obj, RtnInitId, tag.x, tag.y,strTmp,tag.h,tag.t);

       }

   这里用到了C++/C中调用Java中的构造函数,实际上C++/C中还可以调用Java中的其它函数,后面会提到。

2. C++/C指针作为输入参数

   这种情况下就要具体问题,具体分析了。

   如果C++/C代码需要的仅仅是一个地址,且C++/C不会回传给值给Java,那么Java就传递一个int型的数即可。

   例如:

           C++/C:    void setValue(int  *ps32Addr,int value);

   仅仅是给一个地址写值,那么

    Java:public void setValue(int s32Addr,int value);

         JNI:

        JNIEXPORT void  JNICALL Java_setValue_ 1native (JNIEnv *env, jclass thiz, jint s32Addr,jint value)

       {

             int *ps32Addr = (int *)s32Addr;

           setValue(ps32Addr,value);

      }

 

如果C++/C的指针含有从Java中传递的数据信息,就需做如下处理:

typedef struct _Para_tag

{

   int x;

   short y;

   char az[100];

   char h;

}Para_Tag;

C++/C : void setPara(Para_Tag *pstTag)

Java:

public class Para_Tag

{

   int x;

   short y;

   String z;

   char h;

   long t;    

}

public void setPara(Para_Tag   tag);

JNI:

JNIEXPORT void  JNICALL Java_setPara_ 1native (JNIEnv *env, jclass thiz, jobject obj)

{

   Para_Tag  tag;


jclass DataCls = env->FindClass("com/Para_Tag");

jfieldID dataId = env->GetFieldID(DataCls, "x", "I");

tag.x = env->GetIntField(obj, dataId);


dataId = env->GetFieldID(DataCls, "y", "S");

tag.y = env->GetShortField(obj, dataId);


dataId = env->GetFieldID(DataCls, "z", "Ljava/lang/String;");

jstring jstr = (jstring)env->GetObjectField(obj, dataId);

const char* str;

   jboolean isCopye = false;

   if(NULL != jstr)

   {

       str = env->GetStringUTFChars(jstr, &isCopye);

   }

   if(str == NULL)

   {

       return;                  

   }

   strncpy(tag.az,str,100);

   tag.az[99] = '\0';    

   dataId = env->GetFieldID(DataCls, "h", "C");

tag.h = env->GetCharField(obj, dataId);

dataId = env->GetFieldID(DataCls, "t", "J");

tag.t = (jlong)env->GetLongField(obj, dataId);

               setPara(&tag);

}