JNI實戰全面解析

簡介

項目決定移植一款C++開源項目到Android平臺,開始對JNI深刻研究。css

JNI是什麼?html

JNI(Java Native Interface)意爲JAVA本地調用,它容許Java代碼和其餘語言寫的代碼進行交互,簡單的說,一種在Java虛擬機控制下執行代碼的標準機制。
java

NDK是什麼?android

Android NDK(Native Development Kit )是一套工具集合,容許你用像C/C++語言那樣實現應用程序的一部分。c++

爲何要用NDK?
程序員

一、安全性,java是半解釋型語言,很容易被反彙編後拿到源代碼文件,咱們能夠在重要的交互功能使用C語言代替。
二、效率,C語言比起java來講效率要高出不少。
編程

JNI和NDK的區別?
api

從工具上說,NDK其實多了一個把.so和.apk打包的工具,而JNI開發並無打包,只是把.so文件放到文件系統的特定位置。
從編譯庫說NDK開發C/C++只能能使用NDK自帶的有限的頭文件,而使用JNI則可使用文件系統中帶的頭文件。
從編寫方式說,它們同樣。數組

詳解
一、JNI 元素

一、JNI組織結構安全


JNI函數表的組成就像C++的虛函數表,虛擬機能夠運行多張函數表。
JNI接口指針僅在當前線程中起做用,指針不能從一個線程進入另外一個線程,但能夠在不一樣的線程中調用本地方法。


二、原始數據

Jobject  對象 引用類型


Java類型 本地類型(JNI) 描述
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


函數操做

函數 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 語言
Z boolean
B byte
C char
S short
I int
J long
F float
D double



引用類型則爲 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方法報告異常。  

三、JNI函數實戰

一、*.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) 。


二、返回值

[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 ;   返回對象集合  

三、操做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); // 設置該字段的值   

四、回調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); //回調該方法,而且  


五、傳對象到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 ;     

六、與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. }    

四、Android.mk、Application.mk
一、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的路徑



範例:


二、

編譯一個簡單的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)  

二、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)  


編譯問題:SLES/OpenSLES.h: No such file or directory
解決方法:ndk-build TARGET_PLATFORM=android-9


編譯斷點問題:有沒有好用的斷點工具

解決方法:visualGDB 神器

相關文章
相關標籤/搜索