JNI 的類型和數據結構
Java 類型 本地類型 說明
boolean jboolean 無符號,8 位
byte jbyte 無符號,8 位
char jchar 無符號,16 位
short jshort 有符號,16 位
int jint 有符號,32 位
long jlong 有符號,64 位
float jfloat 32 位
double jdouble 64 位
void void N/A
由于JVM中,使用統一的UTF-8編碼,java傳入的String參數,在c文件中被jni轉換為jstring的數據類型,在c文件中聲明char* psz,然后psz = (char*)(*env)->GetStringUTFChars(env, jstring, NULL);c++的操作簡單些,env->GetStringUTFChars( jstring, NULL);注意使用完后,運行(*env)->ReleaseStringUTFChars(env, jstring, psz);
將一個c的字符串轉為jstring,
static char lastfile[80];
JNIEXPORT jstring JNICALL Java_ReadFile_lastFile
(JNIEnv *env, jobject jobj) {
return((*env)->NewStringUTF(env, lastfile));
}
例子
JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
{
char buf[128];
const jbyte *str;
str = (*env)->GetStringUTFChars(env, prompt, NULL);
if (str == NULL) {
return NULL; /* OutOfMemoryError already thrown */
}
printf("%s", str);
(*env)->ReleaseStringUTFChars(env, prompt, str);
/* We assume here that the user does not type more than
* 127 characters */
scanf("%s", buf);
return (*env)->NewStringUTF(env, buf);
}
如果使用Unicode編碼,不需要使用UTF-8處理,使用如下方法
JNIEXPORT jbyteArray JNICALL Java_ReadFile_loadFile
(JNIEnv * env, jobject jobj, jstring name) {
caddr_t m;
jbyteArray jb;
struct stat finfo;
jboolean iscopy;
const jchar *mfile = (*env)->GetStringChars(env,
name, &iscopy);
//...
(*env)->ReleaseStringChars(env, name, mfile);
傳遞Array
boolean
public static void setArray( boolean[] ba )
{
for( int i=0; i < ba.length; i++ )
ba[i] = true;
setArray0( ba );
}
private static native void setArray0( boolean[] ba );
布爾型數組被初始化為 true,本地方法將把特定的元素設置為 false。同時,在 Java 源代碼中,我們可以更改 main 以使其包含測試代碼:
boolean[] ba = new boolean[5];
MyNative.setArray( ba );
for( int i=0; i < ba.length; i++ )
System.out.println( ba[i] );
在編譯源代碼并執行 javah 以后,MyNative.h 頭文件包含以下的原型:
/*
* Class: MyNative
* Method: setArray0
* Signature: ([Z)V
*/
JNIEXPORT void JNICALL Java_MyNative_setArray0
(JNIEnv *, jclass, jbooleanArray);
布爾型數組是作為單個名為 jbooleanArray 的類型創建的。
基本類型有它們自已的數組類型,如 jintArray 和 jcharArray。
非基本類型的數組使用 jobjectArray 類型。下一個示例中包括一個 jobjectArray。這個布爾數組的數組元素是通過 JNI 方法 GetBooleanArrayElements 來訪問的。
針對每種基本類型都有等價的方法。這個本地方法是如下實現的:
JNIEXPORT void JNICALL Java_MyNative_setArray0
(JNIEnv *env, jclass cls, jbooleanArray ba)
{
jboolean* pba = (*env)->GetBooleanArrayElements( env, ba, 0 );
jsize len = (*env)->GetArrayLength(env, ba);
int i=0;
// 更改偶數數組元素
for( i=0; i < len; i+=2 )
pba[i] = JNI_FALSE;
(*env)->ReleaseBooleanArrayElements( env, ba, pba, 0 );
}
指向布爾型數組的指針可以使用 GetBooleanArrayElements 獲得。
數組大小可以用 GetArrayLength 方法獲得。使用 ReleaseBooleanArrayElements 方法釋放數組。就可以讀取和修改數組元素的值了。
int 數組
class IntArray {
private native int sumArray(int[] arr);
public static void main(String[] args) {
IntArray p = new IntArray();
int arr[] = new int[10];
for (int i = 0; i < 10; i++) {
arr[i] = i;
}
int sum = p.sumArray(arr);
System.out.println("sum = " + sum);
}
static {
System.loadLibrary("jnistudy");
}
}
jni的c程序
#include <jni.h>
#include <stdio.h>
#include "IntArray.h"
JNIEXPORT jint JNICALL
Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)
{
jint *carr;
jint i, sum = 0;
carr = (*env)->GetIntArrayElements(env, arr, NULL);
if (carr == NULL) {
return 0; /* exception occurred */
}
for (i=0; i<10; i++) {
sum += carr[i];
}
(*env)->ReleaseIntArrayElements(env, arr, carr, 0);
return sum;
}
GetIntArrayRegion得到數組,ReleaseIntArrayElements釋放資源,GetArrayLength得到數組的長度,SetIntArrayRegion修改數組元素的值。
多位int 數組
class ObjectArrayTest {
private static native int[][] initInt2DArray(int size);
public static void main(String[] args) {
int[][] i2arr = initInt2DArray(3);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
System.out.print(" " + i2arr[i][j]);
}
System.out.println();
}
}
static {
System.loadLibrary("jnistudy");
}
}
jni的代碼
#include <jni.h>
#include <stdio.h>
#include "ObjectArrayTest.h"
JNIEXPORT jobjectArray JNICALL
Java_ObjectArrayTest_initInt2DArray(JNIEnv *env,
jclass cls,
jint size)
{
jobjectArray result;
int i;
jclass intArrCls = (*env)->FindClass(env, "[I");
if (intArrCls == NULL) {
return NULL; /* exception thrown */
}
result = (*env)->NewObjectArray(env, size, intArrCls, NULL);
if (result == NULL) {
return NULL; /* out of memory error thrown */
}
for (i = 0; i < size; i++) {
jint tmp[256]; /* make sure it is large enough! */
int j;
jintArray iarr = (*env)->NewIntArray(env, size);
if (iarr == NULL) {
return NULL; /* out of memory error thrown */
}
for (j = 0; j < size; j++) {
tmp[j] = i + j;
}
(*env)->SetIntArrayRegion(env, iarr, 0, size, tmp);
(*env)->SetObjectArrayElement(env, result, i, iarr);
(*env)->DeleteLocalRef(env, iarr);
}
return result;
}
Get/SetObjectArrayElement得到和設置數組的元素的值。
傳遞 Java String 數組
本例將通過最常用的非基本類型,Java String,說明如何訪問非基本對象的數組。字符串數組被傳遞給本地方法,而本地方法只是將它們顯示到控制臺上。
MyNative 類定義中添加了以下幾個方法:
public static void showStrings( String[] sa )
{
showStrings0( sa );
}
private static void showStrings0( String[] sa );
測試
String[] sa = new String[] { "Hello,", "world!", "JNI", "is", "fun." };
MyNative.showStrings( sa );
c代碼中,本地方法分別訪問每個元素,
JNIEXPORT void JNICALL Java_MyNative_showStrings0
(JNIEnv *env, jclass cls, jobjectArray sa)
{
int len = (*env)->GetArrayLength( env, sa );
int i=0;
for( i=0; i < len; i++ )
{
jobject obj = (*env)->GetObjectArrayElement(env, sa, i);
jstring str = (jstring)obj;
const char* szStr = (*env)->GetStringUTFChars( env, str, 0 );
printf( "%s ", szStr );
(*env)->ReleaseStringUTFChars( env, str, szStr );
}
printf( "\n" );
}
數組元素可以通過 GetObjectArrayElement 訪問。 因為返回值是 jstring 類型,所以可以安全地將它從 jobject 類型轉換為 jstring 類型。
返回 Java String 數組
在本地代碼中創建一個字符串數組并將它返回給 Java 調用者。MyNative.java 中添加了以下幾個方法:
public static String[] getStrings()
{
return getStrings0();
}
private static native String[] getStrings0();
使用showStrings 將 getStrings 的輸出顯示出來:
MyNative.showStrings( MyNative.getStrings() );
實現的本地方法返回五個字符串。
JNIEXPORT jobjectArray JNICALL Java_MyNative_getStrings0
(JNIEnv *env, jclass cls)
{
jstring str;
jobjectArray args = 0;
jsize len = 5;
char* sa[] = { "Hello,", "world!", "JNI", "is", "fun" };
int i=0;
args = (*env)->NewObjectArray(env, len, (*env)->FindClass(env, "java/lang/String"), 0);
for( i=0; i < len; i++ )
{
str = (*env)->NewStringUTF( env, sa[i] );
(*env)->SetObjectArrayElement(env, args, i, str);
}
return args;
}
字符串數組是通過調用 NewObjectArray 創建的,同時傳遞了 String 類和數組長度兩個參數。Java String 是使用 NewStringUTF 創建的。String 元素是使用 SetObjectArrayElement 存入數組中的。
另外要通過jni來改變java的參數引用,例如有參數jbyteArray account,修改完account的值后,java要獲取該值,直接使用
jbyte* jbAccount = (env)->GetByteArrayElements(env, account, 0);
char* szAccount = (char*)jbAccount;
指針的地址并不是account的地址,最后賦下值才行
env->SetByteArrayRegion(account,0,strlen(szAccount),jbyte* jbAccount);