Android是由Google領導開發的操做系統,Android依靠其開放性,迅速普及,成爲目前最流行的智能手機操做系統。html
圖0-1 Android系統架構圖java
圖0-1是Android系統架構圖。大多數程序位於最上層的Java Application層。Android經過把系統劃分爲幾個層次從而使得開發者可使用平臺無關的Java語言進行Android應用開發,沒必要關心程序實際的硬件環境。 Google不只爲開發者提供了SDK開發套件,爲了能讓開發者使用C/C++編寫的本地化的共享庫,利用編譯後的共享庫更高效的完成計算密集型的操做來提升應用的性能,或者移植重用已有的C/C++組件,提升開發效率,Android 1.5以後,又推出了NDK(Native Development Kit)。有了NDK,開發者可以在Android平臺上使用JNI(Java Native Interface)技術,實現應用程序中調用本地二進制共享庫。 因爲Android系統不一樣於以往的JNI使用環境而是在嵌入式硬件環境下,Android NDK提供了一套交叉編譯工具鏈,和構建程序的工具方便開發者在桌面環境下編譯目標平臺的二進制共享庫。 目前NDK提供了對ARMv5TE,ARMv7-A,x86和MIPS指令集平臺的支持,同時在本地接口的支持上,目前如下本地接口支持linux
由上面的介紹,咱們能夠知道,實際上NDK開發是以JNI技術爲基礎的,所以要求開發者必需要掌握基本的JNI技術,這樣才能進行有效的NDK開發。android
JNI(Java Native Interface)是Java SDK 1.1時正式推出的,目的是爲不一樣JVM實現間提供一個標準接口,從而使Java應用可使用本地二進制共享庫,擴充了原有JVM的能力,同時Java程序仍然無需再次編譯就能夠運行在其餘平臺上,即保持了平臺獨立性又能使用平臺相關的本地共享庫提高性能。在Java開發中的位置以下圖所示。JNI做爲鏈接平臺獨立的Java層(如下簡稱Java層)與與平臺相關的本地環境(如下簡稱Native層)之間的橋樑。數組
圖1-1 JNI在Java開發中的位置緩存
實際上在Android內部就大量的使用了JNI技術,尤爲是在Libraries層和Framework層。數據結構
Google在其文檔提到了NDK不能讓大多數應用獲益,其增長的複雜度遠大於得到的性能的代價。Google建議當須要作大量的cpu密集同時少許存儲操做或者重用C/C++代碼時能夠考慮使用NDK。 本文的餘下部分將具體介紹Android平臺下經過NDK的支持的如何進行JNI的開發。架構
本節經過一個簡單的例子,介紹NDK開發流程以及JNI的基本使用。 筆者假定你已經下載了NDK,且有Android SDK開發的經驗。 在NDK開發包中就有若干的NDK示例。其中hello-jni
是一個簡單的實例。該實例從native層傳遞字符串到java層,並顯示在界面上。(你能夠在Eclipse裏選擇 新建Anroid項目 ,以後選擇 「Create project from existing source」,並定位到NDK目錄中的 Sample/hello-jni ,這樣就能夠將示例代碼導入到Eclipse中。) HelloJni的Java代碼以下:oracle
package com.example.hellojni; import android.app.Activity; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.os.Bundle; import android.view.View.OnClickListener; public class HelloJni extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button btn = (Button)findViewById(R.id.btn); final TextView txtv = (TextView)findViewById(R.id.txtv); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { txtv.setText(stringFromJNI());//調用native函數 } }); } /* A native method that is implemented by the * 'hello-jni' native library, which is packaged * with this application. * 聲明含有native關鍵詞的函數,就能夠在類中使用了。 */ public native String stringFromJNI(); /* * 該函數並無在共享庫中實現,可是仍然能夠聲明。 * 沒有實現的native函數也能夠在類中聲明,native方法僅在首次調用時纔開始搜索。 * 若沒有找到該方法,會拋出java.lang.UnsatisfiedLinkError異常 */ public native String unimplementedStringFromJNI(); /* this is used to load the 'hello-jni' library on application * startup. The library has already been unpacked into * /data/data/com.example.HelloJni/lib/libhello-jni.so at * installation time by the package manager. * 使用靜態方式再建立類時就載入共享庫,該共享庫(後面會介紹)在程序安裝後 * 位於/data/data/com.example.HelloJni/lib/libhello-jni.so */ static { System.loadLibrary("hello-jni"); } }
Java代碼中調用native函數很簡單。大體分爲如下幾步:app
JNI的使用的一個關鍵點是 1) 如何找到共享庫 2)如何將Java代碼中的聲明的native方法和實際的C/C++共享庫中的代碼相關聯,即JNI函數註冊。 第一個問題能夠交給NDK構建工具 ndk-build
解決:一般是將編譯好的so共享庫放在 libs/armeabi/libXXX.so
以後會有更詳細的介紹。第二個問題能夠將在第二節中系統講述,如今咱們只簡單的說一下如何作。
簡易實用的方法是經過利用Java提供的 javah
工具生成和聲明的native函數對應的頭文件。具體操做是以下:
bin/
目錄,應該是編譯好的。jni
子目錄,若是沒有則建立一個(咱們如今使用的自帶的實例代碼,所以能夠)。javah -jni com.example.hellojni.HelloJNI -classpath bin/classes -o jni/hello-jni.h
確認javah所在路徑已經在的$PATH路徑下jni
目錄下生成一個名爲 my_jni_header.h
的頭文件。上一步驟咱們獲得了與Java源文件對應的頭文件,所以只要編寫 my_jni_header.c
,實現頭文件裏面的聲明的源代碼。生成的內容以下:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_example_hellojni_HelloJni */ #ifndef _Included_com_example_hellojni_HelloJni #define _Included_com_example_hellojni_HelloJni #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_hellojni_HelloJni * Method: stringFromJNI * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI (JNIEnv *, jobject); /* * Class: com_example_hellojni_HelloJni * Method: unimplementedStringFromJNI * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_unimplementedStringFromJNI (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
能夠看到生成的頭文件中的函數和示例項目 hello-jni
中的 hello-jni.c
正好對應。據此也可知咱們生成的頭文件是正確的。 hello-jni.c
源代碼以下:
#include <string.h> #include <jni.h> #include <stdio.h> /* This is a trivial JNI example where we use a native method * to return a new VM String. See the corresponding Java source * file located at: * * apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java */ jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, jobject thiz ) { char msg[100]; sprintf(msg,"Hello from JNI."); return (*env)->NewStringUTF(env, msg); }
通過以上兩步,咱們已經獲得了C/C++共享庫的源代碼,如今須要使用交叉編譯工具將其編譯成目標機器上的二進制共享庫。NDK工具提供了一個簡單的構建系統,開發者之須要編寫 Android.mk
,以後在項目根目錄下執行命令 ndk-build
就能夠完成交叉編譯過程。
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello-jni LOCAL_SRC_FILES := hello-jni2.c include $(BUILD_SHARED_LIBRARY)
Android.mk
能夠看做是小型的makefile,關於 Android.mk
的更多細節,限於篇幅,這裏不作詳細介紹請參考NDK自帶文檔,裏面有完整的介紹。 輸出的信息相似下面:
Gdbserver : [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver Gdbsetup : libs/armeabi/gdb.setup Compile thumb : hello-jni <= hello-jni.c SharedLibrary : libhello-jni.so Install : libhello-jni.so => libs/armeabi/libhello-jni.so
上面的信息告訴咱們生成好的so文件路徑爲 libs/armeabi/libhello-jni.so
。至此一個簡單的NDK程序的已經制做完成。 總結一下大體過程是:
javah
工具生成頭文件ndk-build
完成共享庫的編譯上一節咱們經過一個簡單的實例,對NDK開發有了一個感性的認識。可是你也許會發現Java層上聲明的native函數與native上面的實現之間的關聯是經過javah生成頭文件完成的,這個方法顯得很笨拙。 實際上這種靜態註冊的方法是經過函數名(Java_com_example_hellojni_HelloJni_stringFromJNI
)來創建聯繫。這種作法有諸多弊端:
Android內部實現上,在使用JNI時很顯然並無這樣作,它採用了更加規範的 動態註冊
的方法進行兩個層次上的關聯。
如下代碼是上面的 hell-jni.c
的動態註冊版,代碼中使用的是自定義的native函數名稱。
#include <string.h> #include <jni.h> #include <stdio.h> jstring getHelloString(); static JNINativeMethod gMethods[] = { { "stringFromJNI", "()Ljava/lang/String;", (void *)getHelloString } }; static int nMethods = 1; static JNIEnv *env = NULL; jstring getHelloString() { char msg[100]; sprintf(msg,"Hello from JNI."); return (*env)->NewStringUTF(env, msg); } jint JNI_OnLoad(JavaVM *vm,void *reserved){ jint result = -1; jclass clz = NULL; if ((*vm)->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK){ return -1; } clz = (*env)->FindClass(env,"com/example/hellojni/HelloJni"); if((*env)->RegisterNatives(env,clz,gMethods,nMethods) < 0) { return -1; } return JNI_VERSION_1_4;//根據JNI規範,JNI_OnLoad必須返回版本號常量不然出錯。 }
根據Java的官方文檔1,當VM載入共享庫時,會尋找 jint JNI_OnLoad(JavaVM *vm, void *reserved)
函數,若是存在則再載入共享庫以後調用該函數。所以咱們能夠在該函數中完成native函數的註冊工做。 JNI_OnLoad
函數的參數有兩個,最主要就是JavaVM
結構。 JavaVM
是存儲VM信息的數據結構。更多信息將在後面講到,這裏咱們只須要知道,經過JavaVM指針咱們能夠獲得另外一個JNI核心結構—— JNIEnv
, JNIEnv
表明了整個JNI環境的數據結構,實際是一個函數表,其中存儲了JNI的各類相關操做的函數指針,後文會詳細介紹,在這裏咱們只須要知道在JNIEnv結構有如下的方法,經過調用就能夠實現動態註冊。
RegisterNatives
用來註冊一組native函數,其中使用到了 JNINativeMethod
結構,具體定義以下3:
typedef struct { char *name; //Java代碼中聲明的native函數的名稱 char *signature; //對應Java代碼層native函數的簽名,下面會介紹 void *fnPtr; //共享庫中函數指針 } JNINativeMethod;
這裏就涉及到了 函數簽名
Java容許函數重載,所以在註冊時就要具體區分出來,不然會出現混亂,於是這裏就要使用一種方法將每一個Java中的方法標上惟一的標記。這種方法就是 函數簽名 。函數簽名應該屬於JVM內部的規範,不具備可讀性。規定4以下:
類型標識 | JAVA類型 |
---|---|
Z | boolean |
B | byte |
C | char |
S | short |
I | int |
J | long |
F | float |
D | double |
L/java/lang/String; | String |
[I | int[] |
[L/java/lang/object; | Object[] |
V | void |
表1 類型標示對應表
每一個函數簽名大體格式 (<參數簽名>)返回值類型簽名
引用類型的參數簽名形式爲 L<包名>
JAVA函數 | 函數簽名 |
---|---|
String f() | ()L/java/lang/String; |
void f(String s,AClass cls,long l) | (L/java/lang/String;L/com/example/AClass;J)V |
String f(byte[]) | ([B)V |
表2 一些簽名示例 函數看起來很難懂,咱們能夠利用 javap
工具查看類中的函數簽名那個信息,具體用法:
$PROJECT/bin/classes
下($PROJECT表明Android程序項目根目錄,並假定java文件已經編譯好,存在bin目錄)javap -s com.example.helljni.HelloJni
其中com.example.hellojni.HelloJni
是類的完整名稱這一節中,經過動態註冊版的hello-jni代碼示例,簡要介紹如何在JNI中實現更靈活的動態註冊方法關聯Java層中native函數和Native層中的實現函數。JNI規範中規定VM在載入共享庫以後,要調用 JNI_OnLoad
函數,通常能夠在共享庫中實現該方法並完成動態註冊。 初步接觸了 JavaVM
結構和 JNIEnv
結構,並瞭解了 JNIEnv
的兩個「函數成員」FindClass
和 registerNatives
。以後還看到了JNI中保存關聯信息的JNINativeMethod
結構以及瞭解了Java的 函數簽名 。
Java層和Native層之間如同兩個說着不一樣語言的國家同樣,若是要互相交流就必需要懂得對方的語言。在Native層中是如何表示Java層的數據類型呢?
JAVA數據類型 | NATIVE層數據類型 | 符號屬性(UNSIGNED/SIGNED) | 長度(BIT) |
---|---|---|---|
boolean | jboolean | unsigned | 8 |
byte | jbyte | unsigned | 8 |
char | jchar | unsigned | 16 |
short | jshort | signed | 16 |
int | jint | signed | 32 |
long | jlong | signed | 64 |
float | jfloat | signed | 32 |
double | jdouble | signed | 64 |
表3 基本數據類型轉換表
JAVA引用類型 | NATIVE類型 |
---|---|
全部object | jobject |
java.lang.Class | jclass |
java.lang.String | jstring |
Object[] | jobjectArray |
boolean[] | jbooleanArray |
byte[] | jbyteArray |
char[] | jcharArray |
short[] | jshortArray |
int[] | jintArray |
long[] | jlongArray |
float[] | jfloatArray |
double[] | jdoubleArray |
java.lang.Throwable | jthrowable |
表4 引用數據類型轉換表 Native層中將除String之外的類都做爲 jobject
處理,對於數組類型,只有基本數據類型的數組是單獨表示,其餘類型的都以 jobjectArray
類型存儲。
http://java.sun.com/docs/books/jni/html/functions.html JavaVM指針指向了一個表明整個VM的實例,同時對全部native線程都有效。主要有如下幾個接口可使用5:
DestroyJavaVM
卸載整個VM實例AttachCurrentThread
將當前的native線程attach到VM實例中,當線程加入到VM線程後,該線程就能夠調用諸如訪問Java對象、調用Java方法等JNI函數DetachCurrentThread
與 AttachCurrentThread
相反GetEnv
既能夠用來檢查當前線程是否已經attach到VM實例,還能夠獲得當前線程的JNIEnv結構。JNIEnv接口包含了JNI的主要功能的函數接口,注意JNIEnv是與線程相關的結構,JNIEnv接口實際是指向內部的一個函數集合,要在Native層操縱某個具體的類,或者調用方法,則須要 JNIEnv
。在native函數的動態註冊方法這一節就使用 JNIEnv
的函數進行了native函數的註冊。 JNIEnv
是指向一個函數表的指針的指針。 其具體定義以下6
typedef const struct JNINativeInterface *JNIEnv; const struct JNINativeInterface ... = { NULL, NULL, NULL, NULL, GetVersion, DefineClass, FindClass, FromReflectedMethod, FromReflectedField, ToReflectedMethod, GetSuperclass, IsAssignableFrom, ToReflectedField, ....//還有不少,這裏略去 };
下圖是 JNIEnv
的一個簡單圖示7
JNIEnv能提供的功能很是多,大致能夠分爲如下幾類5:
限於篇幅,在此沒法一一講解用法。僅說明較經常使用的幾個。更多詳細信息請參考Sun出版的JNI開發者指南(地址)
經過JNIEnv提供的如下方法就能夠調用對象的方法
//調用對象方法的函數原型 NativeType Call<type>Method(JNIEnv *env, jobject obj,jmethodID methodID, ...); NativeType Call<type>MethodA(JNIEnv *env, jobject obj,jmethodID methodID, jvalue *args); NativeType Call<type>MethodV(JNIEnv *env, jobject obj,jmethodID methodID, va_list args); //對對象成員操做的函數原型 NativeType Get<type>Field(JNIEnv *env, jobject obj,jfieldID fieldID); void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID,NativeType value); //取得methodID,fieldId的函數原型 jmethodID GetMethodID(JNIEnv *env, jclass clazz,const char *name, const char *sig); jfieldID GetFieldID(JNIEnv *env, jclass clazz,const char *name, const char *sig);
前三個函數爲一組調用對象方法的函數,區別僅在於傳遞參數的方式不一樣。其中NativeType
表示Java方法返回值對應的Native類型,具體轉換見表3,表4。 <type>
是Void
/ Boolean
/ Int
/ Long
/ Object
等Java基本數據類型。調用這一組函數時,既須要傳遞對象的信息,還要傳遞方法的標識以及Java類中的方法的參數。 jobject
變量既能夠經過在Native層中調用 CallObjectMethod
獲得,也能夠經過後面提到的建立對象實例獲得。 methodId
則能夠經過 GetMethodID
取得。 jclass
參數能夠由前文提到的env->FindClass
函數取得。 相似地,還有 CallStatic<type>Method
、GetStatic<type>Field
、 SetStatic<type>Field
在此再也不贅述。
因爲String特別經常使用,且存在比較複雜的編碼問題,JNI特地將String類做爲一個獨立的Native層中的數據類型jstring處理。同其餘Object操做相似,jstring也是經過 JNIEnv
來管理的。主要的操做函數有:
jstring NewString(JNIEnv *env, const jchar *unicodeChars,jsize len); void ReleaseStringChars(JNIEnv *env, jstring string,const jchar *chars); const jchar * GetStringChars(JNIEnv *env, jstring string,jboolean *isCopy); jsize GetStringLength(JNIEnv *env, jstring string); jstring NewStringUTF(JNIEnv *env, const char *bytes); void ReleaseStringUTFChars(JNIEnv *env, jstring string,const char *utf); const char * GetStringUTFChars(JNIEnv *env, jstring string,jboolean *isCopy); jsize GetStringUTFLength(JNIEnv *env, jstring string);
函數的功能能夠從名稱大體瞭解到,其中 New
開頭的都是將JNI中將String按照編碼分爲兩種,一種是Unicode編碼(UTF-16),一種是UTF-8編碼 須要注意的是Native層中並無垃圾自動回收機制,所以申請字符串資源,用完以後要進行釋放操做,不然會引發內存泄露。 使用過程當中還要注意:Unicode字符串不是「0結尾」的,所以不要依賴\u0000
進行字符串的操做。 常見的錯誤還包括調用 NewStringUTF
傳入的參數 bytes
必須是 Modified UTF-8
格式的,不然會出現亂碼。8
Native層能夠經過操做jarray數據來處理Java層的數組類型。JNI中將基本類型Java數組和引用類型數組分開處理。 下面是幾個Java數組的例子。
int[] iarr; //基本類型數組 float[] farr;//基本類型數組 Object[] oarr;//引用類型數組,數組元素是Object int[][] arr2;//引用類型數組,數組元素是 int[]
下表是基本類型數組操做的函數小結
JNI函數 | 描述 |
---|---|
Get<Type>ArrayRegion | 將基本類型數組的數據複製到預先申請好的C數組中或者反方向操做操做 |
Set<Type>ArrayRegion | |
Get<Type>ArrayElements | 得到/釋放指向基本類型數組的數據的指針 |
Release<Type>ArrayElements | |
GetArrayLength | 返回數組的長度 |
New<Type>Array | 新建一個指定長度的數組 |
GetPrimitiveArrayCritical | 得到/釋放指向基本類型數據的指針 |
ReleasePrimitiveArrayCritical |
表5 基本數據類型數組的操做函數
下面以一個簡單的代碼片斷做爲說明9。假設某段Java代碼中聲明瞭如下的native函數
native int[][] get2DArray(int size);//返回 int[size][size]大小的二維數組
Native層能夠用如下代碼實現
jobjectArray get2DArray(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; }
上述代碼展現了 NewObjectArray
、 NewIntArray
、 SetObjectArrayElement
、SetIntArrayRegion
等函數的用法,代碼可讀性很高,這裏不作進一步解釋。
Java做爲高級語言,具備垃圾自動回收管理機制,內存管理相對輕鬆。而C/C++則沒有這樣的機制,所以在Native層對象實例可能被垃圾回收。這裏就涉及到了JNI的對象引用的管理。 JNI支持三種引用類型—— LocalReference
/ GlobalReference
/WeakGlobalReference
,每一種引用類型的生命週期是不一樣的。 大多數JNI函數使用的是 LocalReference ,即在函數中調用的」New」操做返回的都是對象的 LocalReference
。 LocalReference
只在函數執行代碼範圍內有效,只要JNI函數一返回,引用就會被釋放。相對地, GlobalReference
能夠在多個函數之間共享,直到開發者本身調用釋放函數纔會被垃圾回收。另外一方面 WeakGlobalReference
則具備 引用緩存 功能——一方面它能夠像 GlobalReference
同樣跨函數共享引用,另外一方面它不會阻礙引用的垃圾回收過程。但JNI文檔中建議開發者使用 GlobalReference
和 LocalReference
替代WeakGlobalReference
,由於該引用隨時均可能會被垃圾回收,即便是在調用了IsSameObject
斷定引用有效以後仍然可能會失效10。 有關引用的操做有
//GlobalReference jobject NewGlobalRef(JNIEnv *env, jobject obj); void DeleteGlobalRef(JNIEnv *env, jobject globalRef); //LocalReference void DeleteLocalRef(JNIEnv *env, jobject localRef); jobject NewLocalRef(JNIEnv *env, jobject ref); //WeakLocalReference jweak NewWeakGlobalRef(JNIEnv *env, jobject obj); void DeleteWeakGlobalRef(JNIEnv *env, jweak obj); //通用的引用操做 jobject AllocObject(JNIEnv *env, jclass clazz); jobject NewObject(JNIEnv *env, jclass clazz,jmethodID methodID, ...); jclass GetObjectClass(JNIEnv *env, jobject obj); jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj); jboolean IsSameObject(JNIEnv *env, jobject ref1,jobject ref2);
本文大體介紹了Android NDK的相關技術以及NDK的基礎——JNI的使用,其中簡述了NDK的開發流程、函數註冊的兩種方式、JNI技術的基本內容,其中包括了Java層和Native層之間的數據轉換和互操做方法。不難發現,JNI技術擴展了原有Java技術的能力。
1 Java Native Interface Specificationhttp://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/invocation.html
2http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/functions.html#wp16027
3http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/functions.html#wp17734
4 深刻理解Android:卷I pp28-29
5 Java Native Interface: Programmer’s Guide and Specificationhttp://java.sun.com/docs/books/jni/html/functions.html
6 JNIEnv定義http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/functions.html#wp23720
7 http://java.sun.com/docs/books/jni/html/objtypes.html#5190
8 Android Developers JNI Tipshttp://developer.android.com/guide/practices/design/jni.html#UTF\_8\_and\_UTF\_16\_strings
9 代碼改編自The Java Native Interface Programmer’s Guide and Specificationhttp://java.sun.com/docs/books/jni/html/objtypes.html#27791
10 http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/functions.html#weak