2025-02-10

1.傳遞字符串
到目前為止,我們還沒有實現Java程序向C程序傳遞參數,或者C程序向Java程序傳遞參數。本例程將由Java程序向C程序傳入一個字符串,C程序對該字符串轉成大寫形式後回傳給Java程序。

Java源程序如下。

代碼清單15-6 在Linux平臺上調用C函數的例程——Sample1

      public class Sample1

      {

      public native String stringMethod(String text);

     public static void main(String[] args)

     {

         System.loadLibrary("Sample1");

          Sample1 sample = new Sample1();

          String text   = sample.stringMethod("Thinking In Java");

       System.out.println("stringMethod: " + text);

    }

}

Sample1.java以“Thinking In Java”為參數調用libSample1.so中的函數stringMethod(),在得到返回的字符串後打印輸出。

Sample1.c的源程序如下。

代碼清單15-7 在Linux平臺上調用C函數的例程——Sample1.c

      #include <Sample1.h>

      #include <string.h>   

      JNIEXPORT jstring JNICALL Java_Sample1_stringMethod(JNIEnv *env, jobject obj, jstring string)

      {

          const char *str = (*env)->GetStringUTFChars(env, string, 0);

          char cap[128];

          strcpy(cap, str);

          (*env)->ReleaseStringUTFChars(env, string, str);

       int i=0;

       for(i=0;i<strlen(cap);i++)

         *(cap+i)=(char)toupper(*(cap+i));

       return (*env)->NewStringUTF(env, cap);

    }

首先請註意函數頭部分,函數接收一個jstring類 型的輸入參數,並輸出一個jstring類型的參數。jstring是jni.h中定義的數據類型,是JNI框架內特有的字符串類型,因為jni.h在 Sample1.h中被引入,因此在Sample1.c中無須再次引入。

程序的第4行是從JNI調用上下文中獲取UTF編碼的輸入字符,將其放在指針str所指向 的一段內存中。第9行是釋放這段內存。第13行是將經過大寫轉換的字符串予以返回,這一句使用瞭NewStringUTF()函數,將C語言的字符串指針 轉換為JNI的jstring類型。JNIEnv也是在jni.h中定義的,代表JNI調用的上下文,GetStringUTFChars()、 ReleaseStringUTFChars()和NewStringUTF()均是JNIEnv的函數。

2.遞整型數組
本節例程將首次嘗試在JNI框架內啟用數組:C程序向Java程序返回一個定長的整型數組成的數組,Java程序將該數組打印輸出。

Java程序的源代碼如下。

代碼清單15-8 在Linux平臺上調用C函數的例程——Sample2

       public class Sample2

      {

     public native int[] intMethod();

      public static void main(String[] args)

      {

         System.loadLibrary("Sample2");

         Sample2 sample=new Sample2();

           int[] nums=sample.intMethod();

       for(int i=0;i<nums.length;i++)

      System.out.println(nums[i]);

   }

}

Sample2.java調用libSample2.so中的函數intMethod()。Sample2.c的源代碼如下。

代碼清單15-9 在Linux平臺上調用C函數的例程——Sample2.c

      #include <Sample2.h>

     

      JNIEXPORT jintArray JNICALL Java_Sample2_intMethod(JNIEnv *env, jobject obj)

       {

      inti = 1;

       jintArray array;//定義數組對象

      array = (*env)-> NewIntArray(env, 10);

      for(; i<= 10; i++)

           (*env)->SetIntArrayRegion(env, array, i-1, 1, &i);

  

       /* 獲取數組對象的元素個數 */

    int len = (*env)->GetArrayLength(env, array);

       /* 獲取數組中的所有元素 */

    jint* elems = (*env)-> GetIntArrayElements(env, array, 0);

   for(i=0; i<len; i++)

       printf("ELEMENT %d IS %d\n", i, elems[i]);


   return array;

    }

Sample2.c涉及瞭兩個jni.h定義的整型數 相關的數據類型:jint和jintArray,jint是在JNI框架內特有的整數類型。程序的第7行開辟出一個長度為10 的jint數組。然後依次向該數組中放入元素1-10。第11行至第16行不是程序的必須部分,純粹是為瞭向讀者們演示GetArrayLength() 和GetIntArrayElements()這兩個函數的使用方法,前者是獲取數組長度,後者則是獲取數組的首地址以便於遍歷數組。

3. 傳遞字符串數組
本節例程是對上節例程的進一步深化:雖然仍然是傳遞數組,但是數組的基類換成瞭字符串這樣一種對象數據類型。Java程序將向C程序傳入一個包含中文字符的字符串,C程序並沒有處理這個字符串,而是開辟出一個新的字符串數組返回給Java程序,其中還包含兩個漢字字符串。

Java程序的源代碼如下。

代碼清單15-10 在Linux平臺上調用C函數的例程——Sample3

      public class Sample3

      {

       public native String[] stringMethod(String text);

     


      public static void main(String[] args) throws java.io.UnsupportedEncodingException

       {

         System.loadLibrary("Sample3");

           Sample3 sample = new Sample3();

           String[] texts = sample.stringMethod("java編程思想");

       for(int i=0;i<texts.length;i++)

        {

         texts[i]=new String(texts[i].getBytes("ISO8859-1"),"GBK");

          System.out.print( texts[i] );

       }

       System.out.println();

   }

    }

Sample3.java調用libSample3.so中的函數stringMethod()。Sample3.c的源代碼如下:

代碼清單15-11 在Linux平臺上調用C函數的例程——Sample3.c

       #include <Sample3.h>

      #include <string.h>

      #include <stdlib.h>

      

      #define ARRAY_LENGTH 5
    

     JNIEXPORT jobjectArray JNICALL Java_Sample3_stringMethod(JNIEnv *env, jobject obj, jstring string)

       {  

          jclass objClass = (*env)->FindClass(env, "java/lang/String");

       jobjectArray texts= (*env)->NewObjectArray(env, (jsize)ARRAY_LENGTH, objClass, 0);

       jstring jstr;

        char* sa[] = { "Hello,", "world!", "JNI", "很", "好玩" };

       int i=0;

       for(;i<ARRAY_LENGTH;i++)

        {

          jstr = (*env)->NewStringUTF( env, sa[i] );

         (*env)->SetObjectArrayElement(env, texts, i, jstr);//必須放入jstring

       }

       return texts;

   }

第9、10行是我們需要特別關註的地方:JNI框架並 沒有定義專門的字符串數組,而是使用jobjectArray——對象數組,對象數組的基類是jclass,jclass是JNI框架內特有的類型,相當 於Java語言中的Class類型。在本例程中,通過FindClass()函數在JNI上下文中獲取到java.lang.String的類型 (Class),並將其賦予jclass變量。

在例程中我們定義瞭一個長度為5的對象數組texts,並在程序的第18行向其中循環放入預先定義好的sa數組中的字符串,當然前置條件是使用NewStringUTF()函數將C語言的字符串轉換為jstring類型。

本例程的另一個關註點是C程序向Java程序傳遞的中文字符,在Java程序中能否正常顯 示的問題。在筆者的試驗環境中,Sample3.c是在Linux平臺上編輯的,其中的中文字符則是用支持GBK的輸入法輸入的,而Java程序采用 ISO8859_1字符集存放JNI調用的返回字符,因此在“代碼清單15-10在Linux平臺上調用C函數的例程——Sample3”的第14行中將 其轉碼後輸出。

4.傳遞對象數組
本節例程演示的是C程序向Java程序傳遞對象數組,而且對象數組中存放的不再是字符串,而是一個在Java中自定義的、含有一個topic屬性的MailInfo對象類型。

MailInfo對象定義如下。

代碼清單15-12 在Linux平臺上調用C函數的例程——MailInfo

     public class MailInfo {

      public String topic;

     public String getTopic()

    {

         return this.topic;

      }


public void setTopic(String topic)

   {

    this.topic=topic;

   }

   }

Java程序的源代碼如下。

代碼清單15-13 在Linux平臺上調用C函數的例程——Sample4

       public class Sample4

      {

      public native MailInfo[] objectMethod(String text);

      public static void main(String[] args)

      {

          System.loadLibrary("Sample4");

        Sample4 sample = new Sample4();

          MailInfo[] mails = sample.objectMethod("Thinking In Java");

       for(int i=0;i<mails.length;i++)

         System.out.println(mails[i].topic);

   }

}

Sample4.java調用libSample4.so中的objectMethod()函數。Sample4.c的源代碼如下。

代碼清單15-14 在Linux平臺上調用C函數的例程——Sample4.c

       #include <Sample4.h>

     #include <string.h>

      #include <stdlib.h>

    

      #define ARRAY_LENGTH 5

   

      JNIEXPORT jobjectArray JNICALL Java_Sample4_objectMethod(JNIEnv *env, jobject obj, jstring string)

       { 

         jclass objClass = (*env)->FindClass(env, "java/lang/Object");

       jobjectArray mails= (*env)->NewObjectArray(env, (jsize)ARRAY_LENGTH, objClass, 0);


       jclass objectClass = (*env)->FindClass(env, "MailInfo");

       jfieldID topicFieldId = (*env)->GetFieldID(env, objectClass, "topic", "Ljava/lang/String;");

   


       int i=0;

       for(;i<ARRAY_LENGTH;i++)

    {

      (*env)->SetObjectField(env, obj, topicFieldId, string);

         (*env)->SetObjectArrayElement(env, mails, i, obj);

       }

     


    return mails;

   }

程序的第9、10行讀者們應該不會陌生,在上一節的例 程中已經出現過,不同之處在於這次通過FindClass()函數在JNI上下文中獲取的是java.lang.Object的類型(Class),並將 其作為基類開辟出一個長度為5的對象數組,準備用來存放MailInfo對象。

程序的第12、13行的目的則是創建一個jfieldID類型的變量,在JNI中,操作對 象屬性都是通過jfieldID進行的。第12行首先查找得到MailInfo的類型(Class),然後基於這個jclass進一步獲取其名為 topic的屬性,並將其賦予jfieldID變量。

程序的第18、19行的目的是循環向對象數組中放入jobject對象。 SetObjectField()函數屬於首次使用,該函數的作用是向jobject的屬性賦值,而值的內容正是Java程序傳入的jstring變量 值。請註意在向對象屬性賦值和向對象數組中放入對象的過程中,我們使用瞭在函數頭部分定義的jobject類型的環境參數obj作為中介。至此,JNI框 架固有的兩個環境入參env和obj,我們都有涉及。

5.傳遞byte[]
源C
int SmsSend(char *phonenum,char *contnet);
java的native本地
public native static int SmsSend(byte[] mobileNo,byte[] smContent);
JNIEXPORT jint JNICALL Java_Sample5_objectMethod(JNIEnv *env, jclass jobject, jbyteArray mobileno,jbyteArray smscontent){
char *pSmscontent;
//jsize *theArrayLengthJ=(*env)->GetArrayLength(env,mobileno);
jbyte *arrayBody=(*env)->GetByteArrayElements(env,moblieno,0);
char *pMobileNo=(char *)arrayBody;
printf("[%s]\n,pMobileNo);
//jsize *theArrayLengthJ=(*env)->GetArrayLength(env,smscontent);
arrayBody=(*env)->GetByteArrayElements(env,smscontent,0);
pSmscontent=(char *)arrayBody;
printf("[%s]\n,pSmscontent);
return SmsSend(pMobileNo,pSmscontent);

作者“jykenan”

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *