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”