在上一篇中介紹了JNI的基本數據類型。本文是JNI系列的第三篇,介紹JNI中的除了基本類型之外的引用類型—String類型。
系列文章的大綱如下:
JNI引用類型在JNI定義了一系列的引用類型,與Java中的類型對應,有如下的繼承關係:
// JNI 類型 Java類型
jobject (all Java objects)
|-- jclass (java.lang.Class objects)
|-- jstring (java.lang.String objects
|-- jarray (arrays)
| |-- jobjectArray (object arrays)
| |-- jbooleanArray (boolean arrays)
| |-- jbyteArray (byte arrays)
| |-- jcharArray (char arrays)
| |-- jshortArray (short arrays)
| |-- jintArray (int arrays)
| |-- jlongArray (long arrays)
| |-- jfloatArray (float arrays)
| |-- jdoubleArray (double arrays)
|-- jthrowable (java.lang.Throwable objects)
在C的實現中,所以的JNI引用類型都是jobject,如:
struct _jobject;
typedef struct _jobject *jobject;
typedef jobject jclass;
typedef jobject jthrowable;
typedef jobject jstring;
typedef jobject jarray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jarray jobjectArray;
而在C++的實現中,有類似如下的定義:
class _jobject {};
class _jclass : public _jobject {};
class _jthrowable : public _jobject {};
class _jstring : public _jobject {};
class _jarray : public _jobject {};
class _jbooleanArray : public _jarray {};
class _jbyteArray : public _jarray {};
class _jcharArray : public _jarray {};
class _jshortArray : public _jarray {};
class _jintArray : public _jarray {};
class _jlongArray : public _jarray {};
class _jfloatArray : public _jarray {};
class _jdoubleArray : public _jarray {};
class _jobjectArray : public _jarray {};
typedef _jobject *jobject;
typedef _jclass *jclass;
typedef _jthrowable *jthrowable;
typedef _jstring *jstring;
typedef _jarray *jarray;
typedef _jbooleanArray *jbooleanArray;
typedef _jbyteArray *jbyteArray;
typedef _jcharArray *jcharArray;
typedef _jshortArray *jshortArray;
typedef _jintArray *jintArray;
typedef _jlongArray *jlongArray;
typedef _jfloatArray *jfloatArray;
typedef _jdoubleArray *jdoubleArray;
typedef _jobjectArray *jobjectArray;
可以看到JNI中所以的引用類型都是指針。
還有jvalue的定義,用於數組元素類型的定義:
typedef union jvalue {
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
} jvalue;
對String類型,思考下面兩個問題:
Java中String使用Unicode編碼,C/C++中的char *是UTF-8編碼(Linux/Android/MacOS),如何轉換編碼?
所有引用類型都面對一個問題,內存管理方式不同,Java中是有GC的,C/C++中需要手動管理內存,JNI中怎麼處理?
對於兩個問題,JNI提供相應的函數供我們使用,我們使用時需要特別的小心。
JNI的字符操作方法Unicode編碼字符串jstring NewString(JNIEnv *env, const jchar *unicodeChars, jsize len);
jsize GetStringLength(JNIEnv *env, jstring string);
const jchar * GetStringChars(JNIEnv *env, jstring string, jboolean *isCopy);
void ReleaseStringChars(JNIEnv *env, jstring string,
const jchar *chars);
所有的函數的第一個參數都是JNIEnv *,稱為JNI接口指針,在生成的native方法的中第一個參數都是它。
NewString(),通過Unicode字符數組構造一個jstring,也就是java.lang.String對象。JVM會進行內存管理。len是unicodeChars的長度。
GetStringLength(),返回string中Unicode字符的數量。
GetStringChars(),返回string的表示的Unicode編碼的字符數組。在調用ReleaseStringChars()前都可用。這裡就需要自行進行內存管理。如果isCopy不是空,如果返回的數組是一個拷貝,其值會置為JNI_TRUE,否則置為JNI_FALSE。
ReleaseStringChars(),通知JVM,native代碼不會再使用chars,chars是使用GetStringChars()得到的返回值。
UTF-8編碼字符串UTF-8編碼的字符串與Unicode編碼字符串接口類似。平時使用最多的也是下面的幾個接口。
jstring NewStringUTF(JNIEnv *env, const char *bytes);
jsize GetStringUTFLength(JNIEnv *env, jstring string);
const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);
void ReleaseStringUTFChars(JNIEnv *env, jstring string, const char *utf);
通過下面的實例來加深一下理解。
實例我們來看一個實例。
還是Hello.java
package myjni;
public class Hello {
static {
System.loadLibrary("hello");
}
// 返回字符串:"Hey, name"
public native String hello(String name);
public static void main(String[] args) {
Hello hello = new Hello();
String name = "furzoom";
System.out.println("Say hello to " + name + ": " + hello.hello(name));
name = "楓竹夢";
System.out.println("Say hello to " + name + ": " + hello.hello(name));
}
}
如果還不清楚如果生成頭文件請參考JNI簡介。
生成頭文件myjni_Hello.h的籤名為:
/*
* Class: myjni_Hello
* Method: hello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_myjni_Hello_hello
(JNIEnv *, jobject, jstring);
實現函數Java_myjni_Hello_hello:
JNIEXPORT jstring JNICALL Java_myjni_Hello_hello(JNIEnv *env, jobject obj, jstring jname) {
// 得到傳入的UTF-8編碼的字符串
const char* name = env->GetStringUTFChars(jname, nullptr);
if (name == nullptr) {
return nullptr;
}
// 構造需要返回的結果字符串
std::string result("Hey, ");
result += name;
// 使用完從GetStringUTFChars的字符串後,需要通知JVM
env->ReleaseStringUTFChars(jname, name);
// 從JNI的UTF-8編碼字符串構造jstring
return env->NewStringUTF(result.c_str());
}
編譯生成動態庫,並運行Java程序得到輸出:
Say hello to furzoom: Hey, furzoom
Say hello to 楓竹夢: Hey, 楓竹夢
本文完。