JNI

JNI是Java Native Interface的縮寫,它提供了若干的API實現了Java和其他語言的通信(主要是C&C++)。從Java1.1開始,JNI標準成爲java平臺的一部分,它允許Java代碼和其他語言寫的代碼進行交互。JNI一開始是爲了本地已編譯語言,尤其是C和C++而設計的,但是它並不妨礙你使用其他編程語言,只要調用約定受支持就可以了。使用java與本地已編譯的代碼交互,通常會喪失平臺可移植性。但是,有些情況下這樣做是可以接受的,甚至是必須的。例如,使用一些舊的庫,與硬件、操作系統進行交互,或者爲了提高程序的性能。JNI標準至少要保證本地代碼能工作在任何Java 虛擬機環境下。

JNI可以這樣與本地程序進行交互:
1、你可以使用JNI來實現“本地方法”(native methods),並在JAVA程序中調用它們。
2、JNI支持一個“調用接口”(invocation interface),它允許你把一個JVM嵌入到本地程序中。本地程序可以鏈接一個實現了JVM的本地庫,然後使用“調用接口”執行JAVA語言編寫的軟件模塊。例如,一個用C語言寫的瀏覽器可以在一個嵌入式JVM上面執行從網上下載下來的applets。

Jobject  對象 引用類型



對應數據類型關係如下表:
Java 類型 本地 C 類型
實際表示的 C 類型
(Win32)
說明
boolean jboolean unsigned char 無符號,8 位
byte jbyte signed char 有符號,8 位
char jchar unsigned short 無符號,16 位
short jshort short 有符號,16 位
int jint long 有符號,32 位
long jlong __int64 有符號,64 位
float jfloat float 32 位
double jdouble double 64 位
void void N/A N/A

函數操作

函數 Java 數組類型 本地類型 說明
GetBooleanArrayElements jbooleanArray jboolean ReleaseBooleanArrayElements 釋放
GetByteArrayElements jbyteArray jbyte ReleaseByteArrayElements 釋放
GetCharArrayElements jcharArray jchar ReleaseShortArrayElements 釋放
GetShortArrayElements jshortArray jshort ReleaseBooleanArrayElements 釋放
GetIntArrayElements jintArray jint ReleaseIntArrayElements 釋放
GetLongArrayElements jlongArray jlong ReleaseLongArrayElements 釋放
GetFloatArrayElements jfloatArray jfloat ReleaseFloatArrayElements 釋放
GetDoubleArrayElements jdoubleArray jdouble ReleaseDoubleArrayElements 釋放
GetObjectArrayElement 自定義對象 object  
SetObjectArrayElement 自定義對象 object  
GetArrayLength     獲取數組大小
New<Type>Array     創建一個指定長度的原始數據類型的數組
GetPrimitiveArrayCritical     得到指向原始數據類型內容的指針,該方法可能使垃圾回收不能執行,該方法可能返回數組的拷貝,因此必須釋放此資源。
ReleasePrimitiveArrayCritical     釋放指向原始數據類型內容的指針,該方法可能使垃圾回收不能執行,該方法可能返回數組的拷貝,因此必須釋放此資源。
NewStringUTF     jstring類型的方法轉換
GetStringUTFChars     jstring類型的方法轉換
DefineClass      從原始類數據的緩衝區中加載類
FindClass      該函數用於加載本地定義的類。它將搜索由CLASSPATH 環境變量爲具有指定名稱的類所指定的目錄和 zip文件
GetObjectClass      通過對象獲取這個類。該函數比較簡單,唯一注意的是對象不能爲NULL,否則獲取的class肯定返回也爲NULL
GetSuperclass      獲取父類或者說超類 。 如果 clazz 代表類class而非類 object,則該函數返回由 clazz 所指定的類的超類。 如果 clazz指定類 object 或代表某個接口,則該函數返回NULL
IsAssignableFrom      確定 clazz1 的對象是否可安全地強制轉換爲clazz2
Throw     拋出 java.lang.Throwable 對象
ThrowNew      利用指定類的消息(由 message 指定)構造異常對象並拋出該異常
ExceptionOccurred      確定是否某個異常正被拋出。在平臺相關代碼調用 ExceptionClear() 或 Java 代碼處理該異常前,異常將始終保持拋出狀態
ExceptionDescribe      將異常及堆棧的回溯輸出到系統錯誤報告信道(例如 stderr)。該例程可便利調試操作
ExceptionClear      清除當前拋出的任何異常。如果當前無異常,則此例程不產生任何效果
FatalError      拋出致命錯誤並且不希望虛擬機進行修復。該函數無返回值
NewGlobalRef      創建 obj 參數所引用對象的新全局引用。obj 參數既可以是全局引用,也可以是局部引用。全局引用通過調用DeleteGlobalRef() 來顯式撤消。
DeleteGlobalRef      刪除 globalRef 所指向的全局引用
DeleteLocalRef      刪除 localRef所指向的局部引用
AllocObject      分配新 Java 對象而不調用該對象的任何構造函數。返回該對象的引用。clazz 參數務必不要引用數組類。
getObjectClass     返回對象的類
IsSameObject     測試兩個引用是否引用同一 Java 對象
NewString      利用 Unicode 字符數組構造新的 java.lang.String 對象
GetStringLength      返回 Java 字符串的長度(Unicode 字符數)
GetStringChars      返回指向字符串的 Unicode 字符數組的指針。該指針在調用 ReleaseStringchars() 前一直有效
ReleaseStringChars      通知虛擬機平臺相關代碼無需再訪問 chars。參數chars 是一個指針,可通過 GetStringChars() 從 string 獲得
NewStringUTF      利用 UTF-8 字符數組構造新 java.lang.String 對象
GetStringUTFLength      以字節爲單位返回字符串的 UTF-8 長度
GetStringUTFChars      返回指向字符串的 UTF-8 字符數組的指針。該數組在被ReleaseStringUTFChars() 釋放前將一直有效
ReleaseStringUTFChars      通知虛擬機平臺相關代碼無需再訪問 utf。utf 參數是一個指針,可利用 GetStringUTFChars() 獲得
NewObjectArray      構造新的數組,它將保存類 elementClass 中的對象。所有元素初始值均設爲 initialElement
Set<PrimitiveType>ArrayRegion     將基本類型數組的某一區域從緩衝區中複製回來的一組函數
GetFieldID      返回類的實例(非靜態)域的屬性 ID。該域由其名稱及簽名指定。訪問器函數的
Get<type>Field 及 Set<type>Field系列使用域 ID 檢索對象域。GetFieldID() 不能用於獲取數組的長度域。應使用GetArrayLength()。
Get<type>Field     該訪問器例程系列返回對象的實例(非靜態)域的值。要訪問的域由通過調用GetFieldID() 而得到的域 ID 指定。 
Set<type>Field     該訪問器例程系列設置對象的實例(非靜態)屬性的值。要訪問的屬性由通過調用
SetFieldID() 而得到的屬性 ID指定。
GetStaticFieldID 

GetStatic<type>Field

SetStatic<type>Field
    同上,只不過是靜態屬性。
GetMethodID     返回類或接口實例(非靜態)方法的方法 ID。方法可在某個 clazz 的超類中定義,也可從 clazz 繼承。該方法由其名稱和簽名決定。 GetMethodID() 可使未初始化的類初始化。要獲得構造函數的方法 ID,應將<init> 作爲方法名,同時將void (V) 作爲返回類型。
CallVoidMethod      
CallObjectMethod      
CallBooleanMethod       
CallByteMethod      
CallCharMethod      
CallShortMethod      
CallIntMethod      
CallLongMethod      
CallFloatMethod      
CallDoubleMethod      
GetStaticMethodID      調用靜態方法
Call<type>Method      
RegisterNatives      向 clazz 參數指定的類註冊本地方法。methods 參數將指定 JNINativeMethod 結構的數組,其中包含本地方法的名稱、簽名和函數指針。nMethods 參數將指定數組中的本地方法數。
UnregisterNatives      取消註冊類的本地方法。類將返回到鏈接或註冊了本地方法函數前的狀態。該函數不應在常規平臺相關代碼中使用。相反,它可以爲某些程序提供一種重新加載和重新鏈接本地庫的途徑。    
       

域描述符(返回值類型簽名)


    Java類型      對應的簽名
boolean Z
byte B
char C
shrot S
int I
long L
float F
double D
void V
Object L用/分割包的完整類名;  Ljava/lang/String;
Array [簽名       [I       [Ljava/lang/String;

引用類型則爲 L + 該類型類描述符 + 。

數組,其爲 :  [ + 其類型的域描述符 + 。

多維數組則是 n個[ +該類型的域描述符 , N代表的是幾維數組。

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. String類型的域描述符爲 Ljava/lang/String;    
  2.   
  3. [ + 其類型的域描述符 + ;  
  4. int[ ]     其描述符爲[I  
  5. float[ ]   其描述符爲[F  
  6. String[ ]  其描述符爲[Ljava/lang/String;  
  7. Object[ ]類型的域描述符爲[Ljava/lang/Object;  
  8. int  [ ][ ] 其描述符爲[[I  
  9. float[ ][ ] 其描述符爲[[F  

 將參數類型的域描述符按照申明順序放入一對括號中後跟返回值類型的域描述符,規則如下: (參數的域描述符的疊加)返回類型描述符。對於,沒有返回值的,用V(表示void型)表示。
舉例如下:

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. Java層方法                                               JNI函數簽名  
  2.                 String test ( )                                              Ljava/lang/String;  
  3.                 int f (int i, Object object)                            (ILjava/lang/Object;)I  
  4.                 void set (byte[ ] bytes)                                ([B)V  

JNIEnv與JavaVM 

JNIEnv 概念 : 是一個線程相關的結構體, 該結構體代表了 Java 在本線程的運行環境 ; 

JNIEnv 與 JavaVM : 注意區分這兩個概念; 
-- JavaVM : JavaVM 是 Java虛擬機在 JNI 層的代表, JNI 全局只有一個;
-- JNIEnv : JavaVM 在線程中的代表, 每個線程都有一個, JNI 中可能有很多個 JNIEnv;

JNIEnv 作用 : 
-- 調用 Java 函數 : JNIEnv 代表 Java 運行環境, 可以使用 JNIEnv 調用 Java 中的代碼;
-- 操作 Java 對象 : Java 對象傳入 JNI 層就是 Jobject 對象, 需要使用 JNIEnv 來操作這個 Java 對象;


JNIEnv 體系結構 

線程相關 : JNIEnv 是線程相關的, 即 在 每個線程中 都有一個 JNIEnv 指針, 每個JNIEnv 都是線程專有的, 其它線程不能使用本線程中的 JNIEnv, 線程 A 不能調用 線程 B 的 JNIEnv;

JNIEnv 不能跨線程 : 
-- 當前線程有效 : JNIEnv 只在當前線程有效, JNIEnv 不能在 線程之間進行傳遞, 在同一個線程中, 多次調用 JNI層方法, 傳入的 JNIEnv 是相同的;
-- 本地方法匹配多JNIEnv : 在 Java 層定義的本地方法, 可以在不同的線程調用, 因此 可以接受不同的 JNIEnv;

JNIEnv 結構 : 由上面的代碼可以得出, JNIEnv 是一個指針,  指向一個線程相關的結構, 線程相關結構指向 JNI 函數指針 數組, 這個數組中存放了大量的 JNI 函數指針, 這些指針指向了具體的 JNI 函數; 


注意:JNIEnv只在當前線程中有效。本地方法不能將JNIEnv從一個線程傳遞到另一個線程中。相同的 Java 線程中對本地方法多次調用時,傳遞給該本地方法的JNIEnv是相同的。但是,一個本地方法可被不同的 Java 線程所調用,因此可以接受不同的 JNIEnv。



UTF-8編碼

JNI使用改進的UTF-8字符串來表示不同的字符類型。Java使用UTF-16編碼。UTF-8編碼主要使用於C語言,因爲它的編碼用\u000表示爲0xc0,而不是通常的0×00。非空ASCII字符改進後的字符串編碼中可以用一個字節表示。

錯誤

JNI不會檢查NullPointerException、IllegalArgumentException這樣的錯誤,原因是:導致性能下降。

在絕大多數C的庫函數中,很難避免錯誤發生。
JNI允許用戶使用Java異常處理。大部分JNI方法會返回錯誤代碼但本身並不會報出異常。因此,很有必要在代碼本身進行處理,將異常拋給Java。在JNI內部,首先會檢查調用函數返回的錯誤代碼,之後會調用ExpectOccurred()返回一個錯誤對象。
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. jthrowable ExceptionOccurred(JNIEnv *env);  
  2. 例如:一些操作數組的JNI函數不會報錯,因此可以調用ArrayIndexOutofBoundsException或ArrayStoreExpection方法報告異常。  
3、JNI函數實戰

1、*.so的入口函數

JNI_OnLoad()與JNI_OnUnload()
當Android的VM(Virtual Machine)執行到System.loadLibrary()函數時,首先會去執行C組件裏的JNI_OnLoad()函數。它的用途有二:
(1)告訴VM此C組件使用那一個JNI版本。如果你的*.so檔沒有提供JNI_OnLoad()函數,VM會默認該*.so檔是使用最老的JNI 1.1版本。由於新版的JNI做了許多擴充,如果需要使用JNI的新版功能,例如JNI 1.4的java.nio.ByteBuffer,就必須藉由JNI_OnLoad()函數來告知VM。
(2)由於VM執行到System.loadLibrary()函數時,就會立即先呼叫JNI_OnLoad(),所以C組件的開發者可以藉由JNI_OnLoad()來進行C組件內的初期值之設定(Initialization) 。


2、返回值

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. jstring str = env->newStringUTF("HelloJNI");  //直接使用該JNI構造一個jstring對象返回    
  2.     return str ;    
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. jobjectArray ret = 0;  
  2. jsize len = 5;  
  3. jstring str;  
  4. string value("hello");  
  5.    
  6. ret = (jobjectArray)(env->NewObjectArray(len, env->FindClass("java/lang/String"), 0));  
  7. for(int i = 0; i < len; i++)  
  8. {  
  9.     str = env->NewStringUTF(value..c_str());  
  10.     env->SetObjectArrayElement(ret, i, str);  
  11. }  
  12. return ret; 返回數組  
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. jclass    m_cls   = env->FindClass("com/ldq/ScanResult");    
  2.    
  3.    jmethodID m_mid   = env->GetMethodID(m_cls,"<init>","()V");    
  4.        
  5.    jfieldID  m_fid_1 = env->GetFieldID(m_cls,"ssid","Ljava/lang/String;");    
  6.    jfieldID  m_fid_2 = env->GetFieldID(m_cls,"mac","Ljava/lang/String;");    
  7.    jfieldID  m_fid_3 = env->GetFieldID(m_cls,"level","I");    
  8.    
  9.    jobject   m_obj   = env->NewObject(m_cls,m_mid);    
  10.    
  11.                        env->SetObjectField(m_obj,m_fid_1,env->NewStringUTF("AP1"));    
  12.                        env->SetObjectField(m_obj,m_fid_2,env->NewStringUTF("00-11-22-33-44-55"));    
  13.                        env->SetIntField(m_obj,m_fid_3,-50);    
  14.    return m_obj;  返回自定義對象  

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. jclass list_cls = env->FindClass("Ljava/util/ArrayList;");//獲得ArrayList類引用    
  2.     
  3.     if(listcls == NULL)    
  4.     {    
  5.         cout << "listcls is null \n" ;    
  6.     }    
  7.     jmethodID list_costruct = env->GetMethodID(list_cls , "<init>","()V"); //獲得得構造函數Id    
  8.     
  9.     jobject list_obj = env->NewObject(list_cls , list_costruct); //創建一個Arraylist集合對象    
  10.     //或得Arraylist類中的 add()方法ID,其方法原型爲: boolean add(Object object) ;    
  11.     jmethodID list_add  = env->GetMethodID(list_cls,"add","(Ljava/lang/Object;)Z");     
  12.       
  13.     jclass stu_cls = env->FindClass("Lcom/feixun/jni/Student;");//獲得Student類引用    
  14.     //獲得該類型的構造函數  函數名爲 <init> 返回類型必須爲 void 即 V    
  15.     jmethodID stu_costruct = env->GetMethodID(stu_cls , "<init>", "(ILjava/lang/String;)V");    
  16.     
  17.     for(int i = 0 ; i < 3 ; i++)    
  18.     {    
  19.         jstring str = env->NewStringUTF("Native");    
  20.         //通過調用該對象的構造函數來new 一個 Student實例    
  21.         jobject stu_obj = env->NewObject(stucls , stu_costruct , 10,str);  //構造一個對象    
  22.             
  23.         env->CallBooleanMethod(list_obj , list_add , stu_obj); //執行Arraylist類實例的add方法,添加一個stu對象    
  24.     }    
  25.     
  26.     return list_obj ;   返回對象集合  

3、操作Java層的類

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. //獲得jfieldID 以及 該字段的初始值    
  2.    jfieldID  nameFieldId ;    
  3.     
  4.    jclass cls = env->GetObjectClass(obj);  //獲得Java層該對象實例的類引用,即HelloJNI類引用    
  5.     
  6.    nameFieldId = env->GetFieldID(cls , "name" , "Ljava/lang/String;"); //獲得屬性句柄    
  7.     
  8.    if(nameFieldId == NULL)    
  9.    {    
  10.        cout << " 沒有得到name 的句柄Id \n;" ;    
  11.    }    
  12.    jstring javaNameStr = (jstring)env->GetObjectField(obj ,nameFieldId);  // 獲得該屬性的值    
  13.    const char * c_javaName = env->GetStringUTFChars(javaNameStr , NULL);  //轉換爲 char *類型    
  14.    string str_name = c_javaName ;      
  15.    cout << "the name from java is " << str_name << endl ; //輸出顯示    
  16.    env->ReleaseStringUTFChars(javaNameStr , c_javaName);  //釋放局部引用    
  17.     
  18.    //構造一個jString對象    
  19.    char * c_ptr_name = "I come from Native" ;    
  20.        
  21.    jstring cName = env->NewStringUTF(c_ptr_name); //構造一個jstring對象    
  22.     
  23.    env->SetObjectField(obj , nameFieldId , cName); // 設置該字段的值   
4、回調Java層方法

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. jstring str = NULL;    
  2.       
  3.   jclass clz = env->FindClass("cc/androidos/jni/JniTest");    
  4.   //獲取clz的構造函數並生成一個對象    
  5.   jmethodID ctor = env->GetMethodID(clz, "<init>", "()V");    
  6.   jobject obj = env->NewObject(clz, ctor);    
  7.   
  8.   // 如果是數組類型,則在類型前加[,如整形數組int[] intArray,則對應類型爲[I,整形數組String[] strArray對應爲[Ljava/lang/String;    
  9.   jmethodID mid = env->GetMethodID(clz, "sayHelloFromJava", "(Ljava/lang/String;II[I)I");    
  10.   if (mid)    
  11.   {    
  12.       LOGI("mid is get");    
  13.       jstring str1 = env->NewStringUTF("I am Native");    
  14.       jint index1 = 10;    
  15.       jint index2 = 12;    
  16.       //env->CallVoidMethod(obj, mid, str1, index1, index2);    
  17.   
  18.       // 數組類型轉換 testIntArray能不能不申請內存空間    
  19.       jintArray testIntArray = env->NewIntArray(10);    
  20.       jint *test = new jint[10];    
  21.       for(int i = 0; i < 10; ++i)    
  22.       {    
  23.           *(test+i) = i + 100;    
  24.       }    
  25.       env->SetIntArrayRegion(testIntArray, 0, 10, test);    
  26.   
  27.   
  28.       jint javaIndex = env->CallIntMethod(obj, mid, str1, index1, index2, testIntArray);    
  29.       LOGI("javaIndex = %d", javaIndex);    
  30.       delete[] test;    
  31.       test = NULL;    
  32.   }    

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. static void event_callback(int eventId,const char* description) {  //主進程回調可以,線程中回調失敗。  
  2.     if (gEventHandle == NULL)  
  3.         return;  
  4.       
  5.     JNIEnv *env;  
  6.     bool isAttached = false;  
  7.   
  8.     if (myVm->GetEnv((void**) &env, JNI_VERSION_1_2) < 0) { //獲取當前的JNIEnv  
  9.         if (myVm->AttachCurrentThread(&env, NULL) < 0)  
  10.             return;  
  11.         isAttached = true;  
  12.     }  
  13.   
  14.     jclass cls = env->GetObjectClass(gEventHandle); //獲取類對象  
  15.     if (!cls) {  
  16.         LOGE("EventHandler: failed to get class reference");  
  17.         return;  
  18.     }  
  19.   
  20.     jmethodID methodID = env->GetStaticMethodID(cls, "callbackStatic",  
  21.         "(ILjava/lang/String;)V");  //靜態方法或成員方法  
  22.     if (methodID) {  
  23.         jstring content = env->NewStringUTF(description);  
  24.         env->CallVoidMethod(gEventHandle, methodID,eventId,  
  25.             content);  
  26.         env->ReleaseStringUTFChars(content,description);  
  27.     } else {  
  28.         LOGE("EventHandler: failed to get the callback method");  
  29.     }  
  30.   
  31.     if (isAttached)  
  32.         myVm->DetachCurrentThread();  
  33. }  

線程中回調
把c/c++中所有線程的創建,由pthread_create函數替換爲由Java層的創建線程的函數AndroidRuntime::createJavaThread。
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. static pthread_t create_thread_callback(const char* name, void (*start)(void *), void* arg)    
  2. {    
  3.     return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg);    
  4. }   
  5.   
  6.   
  7. static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {  //異常檢測和排除  
  8.     if (env->ExceptionCheck()) {    
  9.         LOGE("An exception was thrown by callback '%s'.", methodName);    
  10.         LOGE_EX(env);    
  11.         env->ExceptionClear();    
  12.     }    
  13. }    
  14.     
  15. static void receive_callback(unsigned char *buf, int len)  //回調  
  16. {    
  17.     int i;    
  18.     JNIEnv* env = AndroidRuntime::getJNIEnv();    
  19.     jcharArray array = env->NewCharArray(len);    
  20.     jchar *pArray ;    
  21.         
  22.     if(array == NULL){    
  23.         LOGE("receive_callback: NewCharArray error.");    
  24.         return;     
  25.     }    
  26.     
  27.     pArray = (jchar*)calloc(len, sizeof(jchar));    
  28.     if(pArray == NULL){    
  29.         LOGE("receive_callback: calloc error.");    
  30.         return;     
  31.     }    
  32.     
  33.     //copy buffer to jchar array    
  34.     for(i = 0; i < len; i++)    
  35.     {    
  36.         *(pArray + i) = *(buf + i);    
  37.     }    
  38.     //copy buffer to jcharArray    
  39.     env->SetCharArrayRegion(array,0,len,pArray);    
  40.     //invoke java callback method    
  41.     env->CallVoidMethod(mCallbacksObj, method_receive,array,len);    
  42.     //release resource    
  43.     env->DeleteLocalRef(array);    
  44.     free(pArray);    
  45.     pArray = NULL;    
  46.         
  47.     checkAndClearExceptionFromCallback(env, __FUNCTION__);    
  48. }  
  49.   
  50.   
  51. public void Receive(char buffer[],int length){  //java層函數  
  52.         String msg = new String(buffer);    
  53.         msg = "received from jni callback" + msg;    
  54.         Log.d("Test", msg);    
  55.     }  

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. jclass cls = env->GetObjectClass(obj);//獲得Java類實例    
  2. jmethodID callbackID = env->GetMethodID(cls , "callback" , "(Ljava/lang/String;)V") ;//或得該回調方法句柄    
  3.   
  4. if(callbackID == NULL)    
  5. {    
  6.      cout << "getMethodId is failed \n" << endl ;    
  7. }    
  8.   
  9. jstring native_desc = env->NewStringUTF(" I am Native");    
  10.   
  11. env->CallVoidMethod(obj , callbackID , native_desc); //回調該方法,並且  


5、傳對象到JNI調用

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. jclass stu_cls = env->GetObjectClass(obj_stu); //或得Student類引用    
  2.   
  3.   if(stu_cls == NULL)    
  4.   {    
  5.       cout << "GetObjectClass failed \n" ;    
  6.   }    
  7.   //下面這些函數操作,我們都見過的。O(∩_∩)O~    
  8.   jfieldID ageFieldID = env->GetFieldID(stucls,"age","I"); //獲得得Student類的屬性id     
  9.   jfieldID nameFieldID = env->GetFieldID(stucls,"name","Ljava/lang/String;"); // 獲得屬性ID    
  10.   
  11.   jint age = env->GetIntField(objstu , ageFieldID);  //獲得屬性值    
  12.   jstring name = (jstring)env->GetObjectField(objstu , nameFieldID);//獲得屬性值    
  13.   
  14.   const char * c_name = env->GetStringUTFChars(name ,NULL);//轉換成 char *    
  15.    
  16.   string str_name = c_name ;     
  17.   env->ReleaseStringUTFChars(name,c_name); //釋放引用    
  18.       
  19.   cout << " at Native age is :" << age << " # name is " << str_name << endl ;     
6、與C++互轉

jbytearray轉c++byte數組

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. jbyte * arrayBody = env->GetByteArrayElements(data,0);     
  2. jsize theArrayLengthJ = env->GetArrayLength(data);     
  3. BYTE * starter = (BYTE *)arrayBody;     


jbyteArray 轉 c++中的BYTE[] 
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. jbyte * olddata = (jbyte*)env->GetByteArrayElements(strIn, 0);    
  2. jsize  oldsize = env->GetArrayLength(strIn);    
  3. BYTE* bytearr = (BYTE*)olddata;    
  4. int len = (int)oldsize;    


C++中的BYTE[]轉jbyteArray 
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. jbyte *by = (jbyte*)pData;    
  2. jbyteArray jarray = env->NewByteArray(nOutSize);    
  3. env->SetByteArrayRegin(jarray, 0, nOutSize, by);    


jbyteArray 轉 char * 
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. char* data = (char*)env->GetByteArrayElements(strIn, 0);    


char* 轉jstring
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. jstring WindowsTojstring(JNIEnv* env, char* str_tmp)    
  2. {    
  3.  jstring rtn=0;    
  4.  int slen = (int)strlen(str_tmp);    
  5.  unsigned short* buffer=0;    
  6.  if(slen == 0)    
  7.  {    
  8.   rtn = env->NewStringUTF(str_tmp);    
  9.  }    
  10.  else    
  11.  {    
  12.   int length = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str_tmp, slen, NULL, 0);    
  13.   buffer = (unsigned short*)malloc(length*2+1);    
  14.   if(MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str_tmp, slen, (LPWSTR)buffer, length) > 0)    
  15.   {    
  16.    rtn = env->NewString((jchar*)buffer, length);    
  17.   }    
  18.  }    
  19.  if(buffer)    
  20.  {    
  21.   free(buffer);    
  22.  }    
  23.  return rtn;    
  24. }    


char* jstring互轉
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. JNIEXPORT jstring JNICALL Java_com_explorer_jni_SambaTreeNative_getDetailsBy    
  2.   (JNIEnv *env, jobject jobj, jstring pc_server, jstring server_user, jstring server_passwd)    
  3. {    
  4.     const char *pc = env->GetStringUTFChars(pc_server, NULL);    
  5.     const char *user = env->GetStringUTFChars(server_user, NULL);    
  6.     const char *passwd = env->GetStringUTFChars(server_passwd, NULL);    
  7.     const char *details = smbtree::getPara(pc, user, passwd);    
  8.     jstring jDetails = env->NewStringUTF(details);    
  9.     return jDetails;    
  10. }    
4、Android.mk、Application.mk
1、Android.mk

Android.mk文件是GNU Makefile的一小部分,它用來對Android程序進行編譯,Android.mk中的變量都是全局的,解析過程會被定義。

一個Android.mk文件可以編譯多個模塊,模塊包括:APK程序、JAVA庫、C\C++應用程序、C\C++靜態庫、C\C++共享庫。

簡單實例如下:

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. LOCAL_PATH := $(call my-dir)  #表示是當前文件的路徑  
  2. include $(CLEAR_VARS)       #指定讓GNU MAKEFILE該腳本爲你清除許多 LOCAL_XXX 變量  
  3. LOCAL_MODULE:helloworld   #標識你在 Android.mk 文件中描述的每個模塊  
  4. MY_SOURCES :foo.c         #自定義變量  
  5. ifneq ($(MY_CONFIG_BAR),)  
  6.  MY_SOURCES += bar.c  
  7. endif  
  8. LOCAL_SRC_FILES += $(MY_SOURCES)    #包含將要編譯打包進模塊中的 C 或 C++源代碼文件  
  9. include $(BUILD_SHARED_LIBRARY) #根據LOCAL_XXX系列變量中的值,來編譯生成共享庫(動態鏈接庫)  


GNU Make系統變量

變量 描述
CLEAR_VARS 指向一個編譯腳本,幾乎所有未定義的 LOCAL_XXX 變量都在"Module-description"節中列出。必須在開始一個新模塊之前包含這個腳本:include$(CLEAR_VARS),用於重置除LOCAL_PATH變量外的,所有LOCAL_XXX系列變量。
BUILD_SHARED_LIBRARY 指向編譯腳本,根據所有的在 LOCAL_XXX 變量把列出的源代碼文件編譯成一個共享庫。
BUILD_STATIC_LIBRARY 一個 BUILD_SHARED_LIBRARY 變量用於編譯一個靜態庫。靜態庫不會複製到的APK包中,但是能夠用於編譯共享庫。
TARGET_ARCH 目標 CPU平臺的名字,  和 android 開放源碼中指定的那樣。如果是arm,表示要生成 ARM 兼容的指令,與 CPU架構的修訂版無關。
TARGET_PLATFORM Android.mk 解析的時候,目標 Android 平臺的名字.詳情可參考/development/ndk/docs/stable- apis.txt.
TARGET_ARCH_ABI 支持目標平臺
TARGET_ABI 目標平臺和 ABI 的組合,它事實上被定義成$(TARGET_PLATFORM)-$(TARGET_ARCH_ABI)  ,在想要在真實的設備中針對一個特別的目標系統進行測試時,會有用。在默認的情況下,它會是'android-3-arm'。
   

模塊描述變量

變量 描述
LOCAL_PATH 這個變量用於給出當前文件的路徑。必須在 Android.mk 的開頭定義,可以這樣使用:LOCAL_PATH := $(call my-dir)  這個變量不會被$(CLEAR_VARS)清除,因此每
個 Android.mk 只需要定義一次(即使在一個文件中定義了幾個模塊的情況下)。
LOCAL_MODULE 這是模塊的名字,它必須是唯一的,而且不能包含空格。必須在包含任一的$(BUILD_XXXX)腳本之前定義它。模塊的名字決定了生成文件的名字。例如,如果一個一個共享庫模塊的名字是,那麼生成文件的名字就是 lib.so。但是,在的 NDK 生成文件中(或者 Android.mk 或者 Application.mk),應該只涉及(引用)有正常名字的其他模塊。
LOCAL_SRC_FILES 這是要編譯的源代碼文件列表。只要列出要傳遞給編譯器的文件,因爲編譯系統自動計算依賴。注意源代碼文件名稱都是相對於 LOCAL_PATH的,你可以使用路徑部分。
LOCAL_CPP_EXTENSION 這是一個可選變量, 用來指定C++代碼文件的擴展名,默認是'.cpp',但是可以改變它。
LOCAL_C_INCLUDES 可選變量,表示頭文件的搜索路徑。
LOCAL_CFLAGS 可選的編譯器選項,在編譯 C 代碼文件的時候使用。
LOCAL_CXXFLAGS 與 LOCAL_CFLAGS同理,針對 C++源文件。
LOCAL_CPPFLAGS 與 LOCAL_CFLAGS同理,但是對 C 和 C++ source files都適用。
LOCAL_STATIC_LIBRARIES 表示該模塊需要使用哪些靜態庫,以便在編譯時進行鏈接。
LOCAL_SHARED_LIBRARIES 表示模塊在運行時要依賴的共享庫(動態庫),在鏈接時就需要,以便在生成文件時嵌入其相應的信息。注意:它不會附加列出的模塊到編譯圖,也就是仍然需要在Application.mk 中把它們添加到程序要求的模塊中。
LOCAL_LDLIBS 編譯模塊時要使用的附加的鏈接器選項。這對於使用‘-l’前綴傳遞指定庫的名字是有用的。
LOCAL_ALLOW_UNDEFINED_SYMBOLS 默認情況下, 在試圖編譯一個共享庫時,任何未定義的引用將導致一個“未定義的符號”錯誤。
LOCAL_ARM_MODE 默認情況下, arm目標二進制會以 thumb 的形式生成(16 位),你可以通過設置這個變量爲 arm如果你希望你的 module 是以 32 位指令的形式。
LOCAL_MODULE_PATH 和 LOCAL_UNSTRIPPED_PATH 在 Android.mk 文件中, 還可以用LOCAL_MODULE_PATH 和LOCAL_UNSTRIPPED_PATH指定最後的目標安裝路徑.
不同的文件系統路徑用以下的宏進行選擇:
  TARGET_ROOT_OUT:表示根文件系統。
   TARGET_OUT:表示 system文件系統。
   TARGET_OUT_DATA:表示 data文件系統。
用法如:LOCAL_MODULE_PATH :=$(TARGET_ROOT_OUT) 
至於LOCAL_MODULE_PATH 和LOCAL_UNSTRIPPED_PATH的區別,暫時還不清楚。

GNU Make 功能宏

變量 描述
my-dir 返回當前 Android.mk 所在的目錄的路徑,相對於 NDK 編譯系統的頂層。
all-subdir-makefiles 返回一個位於當前'my-dir'路徑的子目錄中的所有Android.mk的列表。
this-makefile 返回當前Makefile 的路徑(即這個函數調用的地方)
parent-makefile 返回調用樹中父 Makefile 路徑。即包含當前Makefile的Makefile 路徑。
grand-parent-makefile 返回調用樹中父Makefile的父Makefile的路徑
   

範例:


2、

編譯一個簡單的APK

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. LOCAL_PATH := $(call my-dir)  
  2. include $(CLEAR_VARS)  
  3. # Build all java files in the java subdirectory  
  4. LOCAL_SRC_FILES := $(call all-subdir-java-files)  
  5. # Name of the APK to build  
  6. LOCAL_PACKAGE_NAME :LocalPackage  
  7. # Tell it to build an APK  
  8. include $(BUILD_PACKAGE)  

編譯一個依賴靜態.jar文件的APK 

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. LOCAL_PATH := $(call my-dir)  
  2.   include $(CLEAR_VARS)  
  3.   # List of static libraries to include in the package  
  4.   LOCAL_STATIC_JAVA_LIBRARIES :static-library  
  5.   # Build all java files in the java subdirectory  
  6.   LOCAL_SRC_FILES := $(call all-subdir-java-files)  
  7.   # Name of the APK to build  
  8.   LOCAL_PACKAGE_NAME :LocalPackage  
  9.   # Tell it to build an APK  
  10.   include $(BUILD_PACKAGE)  
  11.  注:LOCAL_STATIC_JAVA_LIBRARIES 後面應是你的APK程序所需要的JAVA庫的JAR文件名。  


編譯一個需要platform key簽名的APK

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. LOCAL_PATH := $(call my-dir)  
  2.   include $(CLEAR_VARS)  
  3.   # Build all java files in the java subdirectory  
  4.   LOCAL_SRC_FILES := $(call all-subdir-java-files)  
  5.   # Name of the APK to build  
  6.   LOCAL_PACKAGE_NAME :LocalPackage  
  7.   LOCAL_CERTIFICATE :platform  
  8.   # Tell it to build an APK  
  9.   include $(BUILD_PACKAGE)  
  10.  注:LOCAL_CERTIFICATE 後面應該是簽名文件的文件名  

編譯一個需要特殊vendor key簽名的APK 

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. LOCAL_PATH := $(call my-dir)  
  2.  include $(CLEAR_VARS)  
  3.  # Build all java files in the java subdirectory  
  4.  LOCAL_SRC_FILES := $(call all-subdir-java-files)  
  5.  # Name of the APK to build  
  6.  LOCAL_PACKAGE_NAME :LocalPackage  
  7.  LOCAL_CERTIFICATE :vendor/example/certs/app  
  8.  # Tell it to build an APK  
  9.  include $(BUILD_PACKAGE)  

裝載一個普通的第三方APK

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. LOCAL_PATH := $(call my-dir)  
  2.  include $(CLEAR_VARS)  
  3.  # Module name should match apk name to be installed.  
  4.  LOCAL_MODULE :LocalModuleName  
  5.  LOCAL_SRC_FILES := $(LOCAL_MODULE).apk  
  6.  LOCAL_MODULE_CLASS :APPS  
  7.  LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)  
  8.  LOCAL_CERTIFICATE :platform  
  9.  include $(BUILD_PREBUILT)   

裝載需要.so(動態庫)的第三方apk

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. LOCAL_PATH := $(my-dir)  
  2. include $(CLEAR_VARS)  
  3. LOCAL_MODULE :baiduinput_android_v1.1_1000e  
  4. LOCAL_SRC_FILES := $(LOCAL_MODULE).apk  
  5. LOCAL_MODULE_CLASS :APPS  
  6. LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)  
  7. LOCAL_CERTIFICATE :platform  
  8. include $(BUILD_PREBUILT)  
  9.    
  10. #################################################################  
  11. ####### copy the library to /system/lib #########################  
  12. #################################################################  
  13. include $(CLEAR_VARS)  
  14. LOCAL_MODULE :libinputcore.so  
  15. LOCAL_MODULE_CLASS :SHARED_LIBRARIES  
  16. LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)  
  17. LOCAL_SRC_FILES :lib/$(LOCAL_MODULE)  
  18. OVERRIDE_BUILD_MODULE_PATH := $(TARGET_OUT_INTERMEDIATE_LIBRARIES)  
  19. include $(BUILD_PREBUILT)  

編譯一個靜態java庫 

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1.   LOCAL_PATH := $(call my-dir)  
  2.   include $(CLEAR_VARS)  
  3.   # Build all java files in the java subdirectory  
  4.   LOCAL_SRC_FILES := $(call all-subdir-java-files)  
  5.   # Any libraries that this library depends on  
  6.   LOCAL_JAVA_LIBRARIES :android.test.runner  
  7.   # The name of the jar file to create  
  8.   LOCAL_MODULE :sample  
  9.   # Build a static jar file.  
  10.   include $(BUILD_STATIC_JAVA_LIBRARY)  
  11. 注:LOCAL_JAVA_LIBRARIES表示生成的java庫的jar文件名。  

編譯C/C++應用程序模板

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. LOCAL_PATH := $(call my-dir)  
  2. #include $(CLEAR_VARS)  
  3. LOCAL_SRC_FILES :main.c  
  4. LOCAL_MODULE :test_exe  
  5. #LOCAL_C_INCLUDES :=  
  6. #LOCAL_STATIC_LIBRARIES :=  
  7. #LOCAL_SHARED_LIBRARIES :=  
  8. include $(BUILD_EXECUTABLE)  
  9. 注:‘:=’是賦值的意思,'+='是追加的意思,‘$’表示引用某變量的值  
  10. LOCAL_SRC_FILES中加入源文件路徑,LOCAL_C_INCLUDES中加入需要的頭文件搜索路徑  
  11. LOCAL_STATIC_LIBRARIES 加入所需要鏈接的靜態庫(*.a)的名稱,  
  12. LOCAL_SHARED_LIBRARIES 中加入所需要鏈接的動態庫(*.so)的名稱,  
  13. LOCAL_MODULE表示模塊最終的名稱,BUILD_EXECUTABLE 表示以一個可執行程序的方式進行編譯。  
  14. (4)編譯C\C++靜態庫  
  15. LOCAL_PATH := $(call my-dir)  
  16. include $(CLEAR_VARS)  
  17. LOCAL_SRC_FILES := \  
  18.  helloworld.c  
  19. LOCAL_MODULE:libtest_static  
  20.  #LOCAL_C_INCLUDES :=  
  21. #LOCAL_STATIC_LIBRARIES :=  
  22. #LOCAL_SHARED_LIBRARIES :=  
  23. include $(BUILD_STATIC_LIBRARY)  
  24. 和上面相似,BUILD_STATIC_LIBRARY 表示編譯一個靜態庫。  

編譯C\C++動態庫的模板

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. LOCAL_PATH := $(call my-dir)  
  2. include $(CLEAR_VARS)  
  3. LOCAL_SRC_FILES :helloworld.c  
  4. LOCAL_MODULE :libtest_shared  
  5. TARGET_PRELINK_MODULES :false  
  6. #LOCAL_C_INCLUDES :=  
  7. #LOCAL_STATIC_LIBRARIES :=  
  8. #LOCAL_SHARED_LIBRARIES :=  
  9. include $(BUILD_SHARED_LIBRARY)  
  10. 和上面相似,BUILD_SHARED_LIBRARY 表示編譯一個共享庫。  
  11. 以上三者的生成結果分別在如下目錄中,generic 依具體 target 會變:  
  12. out/target/product/generic/obj/APPS  
  13. out/target/product/generic/obj/JAVA_LIBRARIES  
  14. out/target/product/generic/obj/EXECUTABLE  
  15. out/target/product/generic/obj/STATIC_LIBRARY  
  16. out/target/product/generic/obj/SHARED_LIBRARY  
  17. 每個模塊的目標文件夾分別爲:  
  18. 1)APK程序:XXX_intermediates  
  19. 2)JAVA庫程序:XXX_intermediates  
  20. 這裏的XXX  
  21.  3)C\C++可執行程序:XXX_intermediates  
  22.  4)C\C++靜態庫: XXX_static_intermediates  
  23.  5)C\C++動態庫: XXX_shared_intermediates  


實例:

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. LOCAL_PATH := $(call my-dir)  #項目地址  
  2. include $(CLEAR_VARS)       #清除變量  
  3.   
  4. LOCAL_MODULE    :libvlcjni    #庫  
  5.   
  6. #源文件  
  7. LOCAL_SRC_FILES :libvlcjni.c libvlcjni-util.c libvlcjni-track.c libvlcjni-medialist.c aout.c vout.c libvlcjni-equalizer.c native_crash_handler.c  
  8. LOCAL_SRC_FILES += thumbnailer.c pthread-condattr.c pthread-rwlocks.c pthread-once.c eventfd.c sem.c  
  9. LOCAL_SRC_FILES += pipe2.c  
  10. LOCAL_SRC_FILES += wchar/wcpcpy.c  
  11. LOCAL_SRC_FILES += wchar/wcpncpy.c  
  12. LOCAL_SRC_FILES += wchar/wcscasecmp.c  
  13. LOCAL_SRC_FILES += wchar/wcscat.c  
  14. LOCAL_SRC_FILES += wchar/wcschr.c  
  15. LOCAL_SRC_FILES += wchar/wcscmp.c  
  16. LOCAL_SRC_FILES += wchar/wcscoll.c  
  17. LOCAL_SRC_FILES += wchar/wcscpy.c  
  18. LOCAL_SRC_FILES += wchar/wcscspn.c  
  19. LOCAL_SRC_FILES += wchar/wcsdup.c  
  20. LOCAL_SRC_FILES += wchar/wcslcat.c  
  21. LOCAL_SRC_FILES += wchar/wcslcpy.c  
  22. LOCAL_SRC_FILES += wchar/wcslen.c  
  23. LOCAL_SRC_FILES += wchar/wcsncasecmp.c  
  24. LOCAL_SRC_FILES += wchar/wcsncat.c  
  25. LOCAL_SRC_FILES += wchar/wcsncmp.c  
  26. LOCAL_SRC_FILES += wchar/wcsncpy.c  
  27. LOCAL_SRC_FILES += wchar/wcsnlen.c  
  28. LOCAL_SRC_FILES += wchar/wcspbrk.c  
  29. LOCAL_SRC_FILES += wchar/wcsrchr.c  
  30. LOCAL_SRC_FILES += wchar/wcsspn.c  
  31. LOCAL_SRC_FILES += wchar/wcsstr.c  
  32. LOCAL_SRC_FILES += wchar/wcstok.c  
  33. LOCAL_SRC_FILES += wchar/wcswidth.c  
  34. LOCAL_SRC_FILES += wchar/wcsxfrm.c  
  35. LOCAL_SRC_FILES += wchar/wmemchr.c  
  36. LOCAL_SRC_FILES += wchar/wmemcmp.c  
  37. LOCAL_SRC_FILES += wchar/wmemcpy.c  
  38. LOCAL_SRC_FILES += wchar/wmemmove.c  
  39. LOCAL_SRC_FILES += wchar/wmemset.c  
  40.   
  41.   
  42. LOCAL_C_INCLUDES := $(VLC_SRC_DIR)/include  #包含頭  
  43.   
  44. ARCH=$(ANDROID_ABI) #變量 平臺  
  45.   
  46. CPP_STATIC=$(ANDROID_NDK)/sources/cxx-stl/gnu-libstdc++$(CXXSTL)/libs/$(ARCH)/libgnustl_static.a #應用靜態庫  
  47.   
  48. LOCAL_CFLAGS :-std=gnu99  #編譯器標識  
  49. ifeq ($(ARCH), armeabi)  
  50.     LOCAL_CFLAGS += -DHAVE_ARMEABI  
  51.     # Needed by ARMv6 Thumb1 (the System Control coprocessor/CP15 is mandatory on ARMv6)  
  52.     # On newer ARM architectures we can use Thumb2  
  53.     LOCAL_ARM_MODE :arm  
  54. endif  
  55. ifeq ($(ARCH), armeabi-v7a)  
  56.     LOCAL_CFLAGS += -DHAVE_ARMEABI_V7A  
  57. endif  
  58. LOCAL_LDLIBS := -L$(VLC_CONTRIB)/lib \  #使用本地庫  
  59.     $(VLC_MODULES) \  
  60.     $(VLC_BUILD_DIR)/lib/.libs/libvlc.a \  
  61.     $(VLC_BUILD_DIR)/src/.libs/libvlccore.a \  
  62.     $(VLC_BUILD_DIR)/compat/.libs/libcompat.a \  
  63.     -ldl -lz -lm -llog \  
  64.     -ldvbpsi -lebml -lmatroska -ltag \  
  65.     -logg -lFLAC -ltheora -lvorbis \  
  66.     -lmpeg2 -la52 \  
  67.     -lavformat -lavcodec -lswscale -lavutil -lpostproc -lgsm -lopenjpeg \  
  68.     -lliveMedia -lUsageEnvironment -lBasicUsageEnvironment -lgroupsock \  
  69.     -lspeex -lspeexdsp \  
  70.     -lxml2 -lpng -lgnutls -lgcrypt -lgpg-error \  
  71.     -lnettle -lhogweed -lgmp \  
  72.     -lfreetype -liconv -lass -lfribidi -lopus \  
  73.     -lEGL -lGLESv2 -ljpeg \  
  74.     -ldvdnav -ldvdread -ldvdcss \  
  75.     $(CPP_STATIC)  
  76.   
  77. include $(BUILD_SHARED_LIBRARY) #編譯成動態庫  
  78.   
  79.   
  80. include $(CLEAR_VARS)   #清除變量  
  81.   
  82. LOCAL_MODULE     :libiomx-gingerbread    
  83. LOCAL_SRC_FILES  := ../$(VLC_SRC_DIR)/modules/codec/omxil/iomx.cpp  
  84. LOCAL_C_INCLUDES := $(VLC_SRC_DIR)/modules/codec/omxil $(ANDROID_SYS_HEADERS_GINGERBREAD)/frameworks/base/include $(ANDROID_SYS_HEADERS_GINGERBREAD)/system/core/include  
  85. LOCAL_CFLAGS     := -Wno-psabi  
  86. LOCAL_LDLIBS     := -L$(ANDROID_LIBS) -lgcc -lstagefright -lmedia -lutils -lbinder  
  87.   
  88. include $(BUILD_SHARED_LIBRARY)  
  89.   
  90. include $(CLEAR_VARS)  
  91.   
  92. LOCAL_MODULE     :libiomx-hc  
  93. LOCAL_SRC_FILES  := ../$(VLC_SRC_DIR)/modules/codec/omxil/iomx.cpp  
  94. LOCAL_C_INCLUDES := $(VLC_SRC_DIR)/modules/codec/omxil $(ANDROID_SYS_HEADERS_HC)/frameworks/base/include $(ANDROID_SYS_HEADERS_HC)/frameworks/base/native/include $(ANDROID_SYS_HEADERS_HC)/system/core/include $(ANDROID_SYS_HEADERS_HC)/hardware/libhardware/include  
  95. LOCAL_CFLAGS     := -Wno-psabi  
  96. LOCAL_LDLIBS     := -L$(ANDROID_LIBS) -lgcc -lstagefright -lmedia -lutils -lbinder  
  97.   
  98. include $(BUILD_SHARED_LIBRARY)  
  99.   
  100. include $(CLEAR_VARS)  
  101.   
  102. LOCAL_MODULE     :libiomx-ics  
  103. LOCAL_SRC_FILES  := ../$(VLC_SRC_DIR)/modules/codec/omxil/iomx.cpp  
  104. LOCAL_C_INCLUDES := $(VLC_SRC_DIR)/modules/codec/omxil $(ANDROID_SYS_HEADERS_ICS)/frameworks/base/include $(ANDROID_SYS_HEADERS_ICS)/frameworks/base/native/include $(ANDROID_SYS_HEADERS_ICS)/system/core/include $(ANDROID_SYS_HEADERS_ICS)/hardware/libhardware/include  
  105. LOCAL_CFLAGS     := -Wno-psabi  
  106. LOCAL_LDLIBS     := -L$(ANDROID_LIBS) -lgcc -lstagefright -lmedia -lutils -lbinder  
  107.   
  108. include $(BUILD_SHARED_LIBRARY)  

2、Application.mk

Application.mk目的是描述在你的應用程序中所需要的模塊(即靜態庫或動態庫)。

變量 描述
APP_PROJECT_PATH 這個變量是強制性的,並且會給出應用程序工程的根目錄的一個絕對路徑。
APP_MODULES 這個變量是可選的,如果沒有定義,NDK將由在Android.mk中聲明的默認的模塊編譯,並且包含所有的子文件(makefile文件)如果APP_MODULES定義了,它不許是一個空格分隔的模塊列表,這個模塊名字被定義在Android.mk文件中的LOCAL_MODULE中。
APP_OPTIM 這個變量是可選的,用來義“release”或"debug"。在編譯您的應用程序模塊的時候,可以用來改變優先級。
APP_CFLAGS 當編譯模塊中有任何C文件或者C++文件的時候,C編譯器的信號就會被髮出。
APP_CXXFLAGS APP_CPPFLAGS的別名,已經考慮在將在未來的版本中廢除了
APP_CPPFLAGS 當編譯的只有C++源文件的時候,可以通過這個C++編譯器來設置
APP_BUILD_SCRIPT 默認情況下,NDK編譯系統會在$(APP_PROJECT_PATH)/jni目錄下尋找名爲Android.mk文件:
$(APP_PROJECT_PATH)/jni/Android.mk
APP_ABI 默認情況下,NDK的編譯系統回味"armeabi"ABI生成機器代碼。
APP_STL 默認情況下,NDK的編譯系統爲最小的C++運行時庫(/system/lib/libstdc++.so)提供C++頭文件。然而,NDK的C++的實現,可以讓你使用或着鏈接在自己的應用程序中。
例如:
APP_STL := stlport_static    --> static STLport library
APP_STL := stlport_shared    --> shared STLport library
APP_STL := system            --> default C++ runtime library
   

實例:

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. APP_OPTIM :release   //調試版還是發行版  
  2. APP_PLATFORM :android-8  //平臺  
  3. APP_STL :gnustl_static  //C++運行時庫  
  4. APP_CPPFLAGS += -frtti      //編譯標識  
  5. APP_CPPFLAGS += -fexceptions  //編譯標識 異常  
  6. APP_CPPFLAGS += -DANDROID   //編譯標識  
  7. APP_MODULES :test     //靜態模塊  
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1.   
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1.   

JNI內存泄漏

JAVA 編程中的內存泄漏,從泄漏的內存位置角度可以分爲兩種:JVM 中 Java Heap 的內存泄漏;JVM 內存中 native memory 的內存泄漏。

Java Heap 的內存泄漏:
Java 對象存儲在 JVM 進程空間中的 Java Heap 中,Java Heap 可以在 JVM 運行過程中動態變化。如果 Java 對象越來越多,佔據 Java Heap 的空間也越來越大,JVM 會在運行時擴充 Java Heap 的容量。如果 Java Heap 容量擴充到上限,並且在 GC 後仍然沒有足夠空間分配新的 Java 對象,便會拋出 out of memory 異常,導致 JVM 進程崩潰。
Java Heap 中 out of memory 異常的出現有兩種原因①程序過於龐大,致使過多 Java 對象的同時存在;②程序編寫的錯誤導致 Java Heap 內存泄漏。

JVM 中 native memory 的內存泄漏
從操作系統角度看,JVM 在運行時和其它進程沒有本質區別。在系統級別上,它們具有同樣的調度機制,同樣的內存分配方式,同樣的內存格局。
JVM 進程空間中,Java Heap 以外的內存空間稱爲 JVM 的 native memory。進程的很多資源都是存儲在 JVM 的 native memory 中,例如載入的代碼映像,線程的堆棧,線程的管理控制塊,JVM 的靜態數據、全局數據等等。也包括 JNI 程序中 native code 分配到的資源。
在 JVM 運行中,多數進程資源從 native memory 中動態分配。當越來越多的資源在 native memory 中分配,佔據越來越多 native memory 空間並且達到 native memory 上限時,JVM 會拋出異常,使 JVM 進程異常退出。而此時 Java Heap 往往還沒有達到上限。
多種原因可能導致 JVM 的 native memory 內存泄漏。
例如:
JVM 在運行中過多的線程被創建,並且在同時運行。
JVM 爲線程分配的資源就可能耗盡 native memory 的容量。
JNI 編程錯誤也可能導致 native memory 的內存泄漏。
Native Code 本身的內存泄漏
JNI 編程首先是一門具體的編程語言,或者 C 語言,或者 C++,或者彙編,或者其它 native 的編程語言。每門編程語言環境都實現了自身的內存管理機制。因此,JNI 程序開發者要遵循 native 語言本身的內存管理機制,避免造成內存泄漏。以 C 語言爲例,當用 malloc() 在進程堆中動態分配內存時,JNI 程序在使用完後,應當調用 free() 將內存釋放。總之,所有在 native 語言編程中應當注意的內存泄漏規則,在 JNI 編程中依然適應。
Native 語言本身引入的內存泄漏會造成 native memory 的內存,嚴重情況下會造成 native memory 的 out of memory。
Global Reference 引入的內存泄漏
JNI 編程還要同時遵循 JNI 的規範標準,JVM 附加了 JNI 編程特有的內存管理機制。
JNI 中的 Local Reference 只在 native method 執行時存在,當 native method 執行完後自動失效。這種自動失效,使得對 Local Reference 的使用相對簡單,native method 執行完後,它們所引用的 Java 對象的 reference count 會相應減 1。不會造成 Java Heap 中 Java 對象的內存泄漏。
而 Global Reference 對 Java 對象的引用一直有效,因此它們引用的 Java 對象會一直存在 Java Heap 中。程序員在使用 Global Reference 時,需要仔細維護對 Global Reference 的使用。如果一定要使用 Global Reference,務必確保在不用的時候刪除。就像在 C 語言中,調用 malloc() 動態分配一塊內存之後,調用 free() 釋放一樣。否則,Global Reference 引用的 Java 對象將永遠停留在 Java Heap 中,造成 Java Heap 的內存泄漏。
LocalReference 的深入理解
Local Reference 在 native method 執行完成後,會自動被釋放,似乎不會造成任何的內存泄漏。但這是錯誤的。


泄漏實例1:創建大量的 JNI Local Reference
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. Java 代碼部分  
  2.  class TestLocalReference {   
  3.  private native void nativeMethod(int i);   
  4.  public static void main(String args[]) {   
  5.          TestLocalReference c = new TestLocalReference();   
  6.          //call the jni native method   
  7.          c.nativeMethod(1000000);   
  8.  }    
  9.  static {   
  10.  //load the jni library   
  11.  System.loadLibrary("StaticMethodCall");   
  12.  }   
  13.  }   
  14.   
  15.   
  16.  JNI 代碼,nativeMethod(int i) 的 C 語言實現  
  17.  #include<stdio.h>   
  18.  #include<jni.h>   
  19.  #include"TestLocalReference.h"  
  20.  JNIEXPORT void JNICALL Java_TestLocalReference_nativeMethod   
  21.  (JNIEnv * env, jobject obj, jint count)   
  22.  {   
  23.  jint i = 0;   
  24.  jstring str;   
  25.   
  26.   
  27.  for(; i<count; i++)   
  28.          str = (*env)->NewStringUTF(env, "0");   
  29.  }   
  30. 運行結果  
  31.  JVMCI161: FATAL ERROR in native method: Out of memory when expanding   
  32.  local ref table beyond capacity   
  33.  at TestLocalReference.nativeMethod(Native Method)   
  34.  at TestLocalReference.main(TestLocalReference.java:9)  

泄漏實例2:建立一個 String 對象,返回給調用函數。
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. JNI 代碼,nativeMethod(int i) 的 C 語言實現  
  2.  #include<stdio.h>   
  3.  #include<jni.h>   
  4.  #include"TestLocalReference.h"  
  5.  jstring CreateStringUTF(JNIEnv * env)   
  6.  {   
  7.  return (*env)->NewStringUTF(env, "0");   
  8.  }   
  9.  JNIEXPORT void JNICALL Java_TestLocalReference_nativeMethod   
  10.  (JNIEnv * env, jobject obj, jint count)   
  11.  {   
  12.  jint i = 0;   
  13.  for(; i<count; i++)   
  14.  {   
  15.          str = CreateStringUTF(env);   
  16.  }   
  17.  }   
  18. 運行結果  
  19.  JVMCI161: FATAL ERROR in native method: Out of memory when expanding local ref   
  20.  table beyond  capacity   
  21.  at TestLocalReference.nativeMethod(Native Method)   
  22.  at TestLocalReference.main(TestLocalReference.java:9)  
發佈了1 篇原創文章 · 獲贊 2 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章