JNI 實戰全面解析
來源:程序員人生 發布時間:2014-11-05 08:44:39 閱讀次數:4668次
簡介
項目決定移植1款C++開源項目到Android平臺,開始對JNI深入研究。
JNI是甚么?
JNI(Java Native Interface)意為JAVA本地調用,它允許Java代碼和其他語言寫的代碼進行交互,簡單的說,1種在Java虛擬機控制下履行代碼的標準機制。
NDK是甚么?
Android NDK(Native Development Kit )是1套工具集合,允許你用像C/C++語言那樣實現利用程序的1部份。
為何要用NDK?
1、安全性,java是半解釋型語言,很容易被反匯編后拿到源代碼文件,我們可以在重要的交互功能使用C語言代替。
2、效力,C語言比起java來講效力要高出很多。
JNI和NDK的區分?
從工具上說,NDK其實多了1個把.so和.apk打包的工具,而JNI開發并沒有打包,只是把.so文件放到文件系統的特定位置。
從編譯庫說,NDK開發C/C++只能能使用NDK自帶的有限的頭文件,而使用JNI則可使用文件系統中帶的頭文件。
從編寫方式說,它們1樣。
詳解
1、JNI 元素
1、JNI組織結構

JNI函數表的組成績像C++的虛函數表,虛擬機可以運行多張函數表。
JNI接口指針僅在當前線程中起作用,指針不能從1個線程進入另外一個線程,但可以在不同的線程中調用本地方法。
2、原始數據
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 | | | 創建1個指定長度的原始數據類型的數組 |
GetPrimitiveArrayCritical | | | 得到指向原始數據類型內容的指針,該方法可能使垃圾回收不能履行,該方法可能返回數組的拷貝,因此必須釋放此資源。 |
ReleasePrimitiveArrayCritical | | | 釋放指向原始數據類型內容的指針,該方法可能使垃圾回收不能履行,該方法可能返回數組的拷貝,因此必須釋放此資源。 |
NewStringUTF | | | jstring類型的方法轉換 |
GetStringUTFChars | | | jstring類型的方法轉換 |
DefineClass | | | 從原始類數據的緩沖區中加載類 |
FindClass | | | 該函數用于加載本地定義的類。它將搜索由CLASSPATH 環境變量為具有指定名稱的類所指定的目錄和 zip文件 |
GetObjectClass | | | 通過對象獲得這個類。該函數比較簡單,唯1注意的是對象不能為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 | | | 測試兩個援用是不是援用同1 Java 對象 |
NewString | | | 利用 Unicode 字符數組構造新的 java.lang.String 對象 |
GetStringLength | | | 返回 Java 字符串的長度(Unicode 字符數) |
GetStringChars | | | 返回指向字符串的 Unicode 字符數組的指針。該指針在調用 ReleaseStringchars() 前1直有效 |
ReleaseStringChars | | | 通知虛擬機平臺相干代碼無需再訪問 chars。參數chars 是1個指針,可通過 GetStringChars() 從 string 取得 |
NewStringUTF | | | 利用 UTF⑻ 字符數組構造新 java.lang.String 對象 |
GetStringUTFLength | | | 以字節為單位返回字符串的 UTF⑻ 長度 |
GetStringUTFChars | | | 返回指向字符串的 UTF⑻ 字符數組的指針。該數組在被ReleaseStringUTFChars() 釋放前將1直有效 |
ReleaseStringUTFChars | | | 通知虛擬機平臺相干代碼無需再訪問 utf。utf 參數是1個指針,可利用 GetStringUTFChars() 取得 |
NewObjectArray | | | 構造新的數組,它將保存類 elementClass 中的對象。所有元素初始值均設為 initialElement |
Set<PrimitiveType>ArrayRegion | | | 將基本類型數組的某1區域從緩沖區中復制回來的1組函數 |
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 | | | 取消注冊類的本地方法。類將返回到鏈接或注冊了本地方法函數前的狀態。該函數不應在常規平臺相干代碼中使用。相反,它可以為某些程序提供1種重新加載和重新鏈接本地庫的途徑。 |
| | | |
域描寫符
域 | Java 語言 |
Z | boolean |
B | byte |
C | char |
S | short |
I | int |
J | long |
F | float |
D | double |
| |
援用類型則為 L + 該類型類描寫符 + 。
數組,其為 : [ + 其類型的域描寫符 + 。
多維數組則是 n個[ +該類型的域描寫符 , N代表的是幾維數組。
String類型的域描寫符為 Ljava/lang/String;
[ + 其類型的域描寫符 + ;
int[ ] 其描寫符為[I
float[ ] 其描寫符為[F
String[ ] 其描寫符為[Ljava/lang/String;
Object[ ]類型的域描寫符為[Ljava/lang/Object;
int [ ][ ] 其描寫符為[[I
float[ ][ ] 其描寫符為[[F
將參數類型的域描寫符依照申明順序放入1對括號中后跟返回值類型的域描寫符,規則以下: (參數的域描寫符的疊加)返回類型描寫符。對,沒有返回值的,用V(表示void型)表示。
舉例以下:
Java層方法 JNI函數簽名
String test ( ) Ljava/lang/String;
int f (int i, Object object) (ILjava/lang/Object;)I
void set (byte[ ] bytes) ([B)V
JNIEnv與JavaVM
JNIEnv 概念 : 是1個線程相干的結構體, 該結構體代表了 Java 在本線程的運行環境 ;
JNIEnv 與 JavaVM : 注意辨別這兩個概念;
-- JavaVM : JavaVM 是 Java虛擬機在 JNI 層的代表, JNI 全局只有1個;
-- JNIEnv : JavaVM 在線程中的代表, 每一個線程都有1個, JNI 中可能有很多個 JNIEnv;
JNIEnv 作用 :
-- 調用 Java 函數 : JNIEnv 代表 Java 運行環境, 可使用 JNIEnv 調用 Java 中的代碼;
-- 操作 Java 對象 : Java 對象傳入 JNI 層就是 Jobject 對象, 需要使用 JNIEnv 來操作這個 Java 對象;
JNIEnv 體系結構
線程相干 : JNIEnv 是線程相干的, 即 在 每一個線程中 都有1個 JNIEnv 指針, 每一個JNIEnv 都是線程專有的, 其它線程不能使用本線程中的 JNIEnv, 線程 A 不能調用 線程 B 的 JNIEnv;
JNIEnv 不能跨線程 :
-- 當前線程有效 : JNIEnv 只在當前線程有效, JNIEnv 不能在 線程之間進行傳遞, 在同1個線程中, 屢次調用 JNI層方法, 傳入的 JNIEnv 是相同的;
-- 本地方法匹配多JNIEnv : 在 Java 層定義的本地方法, 可以在不同的線程調用, 因此 可以接受不同的 JNIEnv;
JNIEnv 結構 : 由上面的代碼可以得出, JNIEnv 是1個指針, 指向1個線程相干的結構, 線程相干結構指向 JNI 函數指針 數組, 這個數組中寄存了大量的 JNI 函數指針, 這些指針指向了具體的 JNI 函數;
注意:JNIEnv只在當前線程中有效。本地方法不能將JNIEnv從1個線程傳遞到另外一個線程中。相同的 Java 線程中對本地方法屢次調用時,傳遞給該本地方法的JNIEnv是相同的。但是,1個本地方法可被不同的 Java 線程所調用,因此可以接受不同的 JNIEnv。

UTF⑻編碼
JNI使用改進的UTF⑻字符串來表示不同的字符類型。Java使用UTF⑴6編碼。UTF⑻編碼主要使用于C語言,由于它的編碼用u000表示為0xc0,而不是通常的0×00。非空ASCII字符改進后的字符串編碼中可以用1個字節表示。
毛病
JNI不會檢查NullPointerException、IllegalArgumentException這樣的毛病,緣由是:致使性能降落。
在絕大多數C的庫函數中,很難避免毛病產生。
JNI允許用戶使用Java異常處理。大部份JNI方法會返回毛病代碼但本身其實不會報出異常。因此,很有必要在代碼本身進行處理,將異常拋給Java。在JNI內部,首先會檢查調用函數返回的毛病代碼,以后會調用ExpectOccurred()返回1個毛病對象。
jthrowable ExceptionOccurred(JNIEnv *env);
例如:1些操作數組的JNI函數不會報錯,因此可以調用ArrayIndexOutofBoundsException或ArrayStoreExpection方法報告異常。
3、JNI函數實戰
1、*.so的入口函數
JNI_OnLoad()與JNI_OnUnload()
當Android的VM(Virtual Machine)履行到System.loadLibrary()函數時,首先會去履行C組件里的JNI_OnLoad()函數。它的用處有2:
(1)告知VM此C組件使用那1個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、返回值
jstring str = env->newStringUTF("HelloJNI"); //直接使用該JNI構造1個jstring對象返回
return str ;
jobjectArray ret = 0;
jsize len = 5;
jstring str;
string value("hello");
ret = (jobjectArray)(env->NewObjectArray(len, env->FindClass("java/lang/String"), 0));
for(int i = 0; i < len; i++)
{
str = env->NewStringUTF(value..c_str());
env->SetObjectArrayElement(ret, i, str);
}
return ret; 返回數組
jclass m_cls = env->FindClass("com/ldq/ScanResult");
jmethodID m_mid = env->GetMethodID(m_cls,"<init>","()V");
jfieldID m_fid_1 = env->GetFieldID(m_cls,"ssid","Ljava/lang/String;");
jfieldID m_fid_2 = env->GetFieldID(m_cls,"mac","Ljava/lang/String;");
jfieldID m_fid_3 = env->GetFieldID(m_cls,"level","I");
jobject m_obj = env->NewObject(m_cls,m_mid);
env->SetObjectField(m_obj,m_fid_1,env->NewStringUTF("AP1"));
env->SetObjectField(m_obj,m_fid_2,env->NewStringUTF("00⑴1⑵2⑶3⑷4⑸5"));
env->SetIntField(m_obj,m_fid_3,⑸0);
return m_obj; 返回自定義對象
jclass list_cls = env->FindClass("Ljava/util/ArrayList;");//取得ArrayList類援用
if(listcls == NULL)
{
cout << "listcls is null
" ;
}
jmethodID list_costruct = env->GetMethodID(list_cls , "<init>","()V"); //取得得構造函數Id
jobject list_obj = env->NewObject(list_cls , list_costruct); //創建1個Arraylist集合對象
//或得Arraylist類中的 add()方法ID,其方法原型為: boolean add(Object object) ;
jmethodID list_add = env->GetMethodID(list_cls,"add","(Ljava/lang/Object;)Z");
jclass stu_cls = env->FindClass("Lcom/feixun/jni/Student;");//取得Student類援用
//取得該類型的構造函數 函數名為 <init> 返回類型必須為 void 即 V
jmethodID stu_costruct = env->GetMethodID(stu_cls , "<init>", "(ILjava/lang/String;)V");
for(int i = 0 ; i < 3 ; i++)
{
jstring str = env->NewStringUTF("Native");
//通過調用該對象的構造函數來new 1個 Student實例
jobject stu_obj = env->NewObject(stucls , stu_costruct , 10,str); //構造1個對象
env->CallBooleanMethod(list_obj , list_add , stu_obj); //履行Arraylist類實例的add方法,添加1個stu對象
}
return list_obj ; 返回對象集合
3、操作Java層的類
//取得jfieldID 和 該字段的初始值
jfieldID nameFieldId ;
jclass cls = env->GetObjectClass(obj); //取得Java層該對象實例的類援用,即HelloJNI類援用
nameFieldId = env->GetFieldID(cls , "name" , "Ljava/lang/String;"); //取得屬性句柄
if(nameFieldId == NULL)
{
cout << " 沒有得到name 的句柄Id
;" ;
}
jstring javaNameStr = (jstring)env->GetObjectField(obj ,nameFieldId); // 取得該屬性的值
const char * c_javaName = env->GetStringUTFChars(javaNameStr , NULL); //轉換為 char *類型
string str_name = c_javaName ;
cout << "the name from java is " << str_name << endl ; //輸出顯示
env->ReleaseStringUTFChars(javaNameStr , c_javaName); //釋放局部援用
//構造1個jString對象
char * c_ptr_name = "I come from Native" ;
jstring cName = env->NewStringUTF(c_ptr_name); //構造1個jstring對象
env->SetObjectField(obj , nameFieldId , cName); // 設置該字段的值
4、回調Java層方法
jstring str = NULL;
jclass clz = env->FindClass("cc/androidos/jni/JniTest");
//獲得clz的構造函數并生成1個對象
jmethodID ctor = env->GetMethodID(clz, "<init>", "()V");
jobject obj = env->NewObject(clz, ctor);
// 如果是數組類型,則在類型前加[,如整形數組int[] intArray,則對應類型為[I,整形數組String[] strArray對應為[Ljava/lang/String;
jmethodID mid = env->GetMethodID(clz, "sayHelloFromJava", "(Ljava/lang/String;II[I)I");
if (mid)
{
LOGI("mid is get");
jstring str1 = env->NewStringUTF("I am Native");
jint index1 = 10;
jint index2 = 12;
//env->CallVoidMethod(obj, mid, str1, index1, index2);
// 數組類型轉換 testIntArray能不能不申請內存空間
jintArray testIntArray = env->NewIntArray(10);
jint *test = new jint[10];
for(int i = 0; i < 10; ++i)
{
*(test+i) = i + 100;
}
env->SetIntArrayRegion(testIntArray, 0, 10, test);
jint javaIndex = env->CallIntMethod(obj, mid, str1, index1, index2, testIntArray);
LOGI("javaIndex = %d", javaIndex);
delete[] test;
test = NULL;
}
static void event_callback(int eventId,const char* description) { //主進程回調可以,線程中回調失敗。
if (gEventHandle == NULL)
return;
JNIEnv *env;
bool isAttached = false;
if (myVm->GetEnv((void**) &env, JNI_VERSION_1_2) < 0) { //獲得當前的JNIEnv
if (myVm->AttachCurrentThread(&env, NULL) < 0)
return;
isAttached = true;
}
jclass cls = env->GetObjectClass(gEventHandle); //獲得類對象
if (!cls) {
LOGE("EventHandler: failed to get class reference");
return;
}
jmethodID methodID = env->GetStaticMethodID(cls, "callbackStatic",
"(ILjava/lang/String;)V"); //靜態方法或成員方法
if (methodID) {
jstring content = env->NewStringUTF(description);
env->CallVoidMethod(gEventHandle, methodID,eventId,
content);
env->ReleaseStringUTFChars(content,description);
} else {
LOGE("EventHandler: failed to get the callback method");
}
if (isAttached)
myVm->DetachCurrentThread();
}
線程中回調
把c/c++中所有線程的創建,由pthread_create函數替換為由Java層的創建線程的函數AndroidRuntime::createJavaThread。
static pthread_t create_thread_callback(const char* name, void (*start)(void *), void* arg)
{
return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg);
}
static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { //異常檢測和排除
if (env->ExceptionCheck()) {
LOGE("An exception was thrown by callback '%s'.", methodName);
LOGE_EX(env);
env->ExceptionClear();
}
}
static void receive_callback(unsigned char *buf, int len) //回調
{
int i;
JNIEnv* env = AndroidRuntime::getJNIEnv();
jcharArray array = env->NewCharArray(len);
jchar *pArray ;
if(array == NULL){
LOGE("receive_callback: NewCharArray error.");
return;
}
pArray = (jchar*)calloc(len, sizeof(jchar));
if(pArray == NULL){
LOGE("receive_callback: calloc error.");
return;
}
//copy buffer to jchar array
for(i = 0; i < len; i++)
{
*(pArray + i) = *(buf + i);
}
//copy buffer to jcharArray
env->SetCharArrayRegion(array,0,len,pArray);
//invoke java callback method
env->CallVoidMethod(mCallbacksObj, method_receive,array,len);
//release resource
env->DeleteLocalRef(array);
free(pArray);
pArray = NULL;
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}
public void Receive(char buffer[],int length){ //java層函數
String msg = new String(buffer);
msg = "received from jni callback" + msg;
Log.d("Test", msg);
}
jclass cls = env->GetObjectClass(obj);//取得Java類實例
jmethodID callbackID = env->GetMethodID(cls , "callback" , "(Ljava/lang/String;)V") ;//或得該回調方法句柄
if(callbackID == NULL)
{
cout << "getMethodId is failed
" << endl ;
}
jstring native_desc = env->NewStringUTF(" I am Native");
env->CallVoidMethod(obj , callbackID , native_desc); //回調該方法,并且
5、傳對象到JNI調用
jclass stu_cls = env->GetObjectClass(obj_stu); //或得Student類援用
if(stu_cls == NULL)
{
cout << "GetObjectClass failed
" ;
}
//下面這些函數操作,我們都見過的。O(∩_∩)O~
jfieldID ageFieldID = env->GetFieldID(stucls,"age","I"); //取得得Student類的屬性id
jfieldID nameFieldID = env->GetFieldID(stucls,"name","Ljava/lang/String;"); // 取得屬性ID
jint age = env->GetIntField(objstu , ageFieldID); //取得屬性值
jstring name = (jstring)env->GetObjectField(objstu , nameFieldID);//取得屬性值
const char * c_name = env->GetStringUTFChars(name ,NULL);//轉換成 char *
string str_name = c_name ;
env->ReleaseStringUTFChars(name,c_name); //釋放援用
cout << " at Native age is :" << age << " # name is " << str_name << endl ;
6、與C++互轉
jbytearray轉c++byte數組
jbyte * arrayBody = env->GetByteArrayElements(data,0);
jsize theArrayLengthJ = env->GetArrayLength(data);
BYTE * starter = (BYTE *)arrayBody;
jbyteArray 轉 c++中的BYTE[]
jbyte * olddata = (jbyte*)env->GetByteArrayElements(strIn, 0);
jsize oldsize = env->GetArrayLength(strIn);
BYTE* bytearr = (BYTE*)olddata;
int len = (int)oldsize;
C++中的BYTE[]轉jbyteArray
jbyte *by = (jbyte*)pData;
jbyteArray jarray = env->NewByteArray(nOutSize);
env->SetByteArrayRegin(jarray, 0, nOutSize, by);
jbyteArray 轉 char *
char* data = (char*)env->GetByteArrayElements(strIn, 0);
char* 轉jstring
jstring WindowsTojstring(JNIEnv* env, char* str_tmp)
{
jstring rtn=0;
int slen = (int)strlen(str_tmp);
unsigned short* buffer=0;
if(slen == 0)
{
rtn = env->NewStringUTF(str_tmp);
}
else
{
int length = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str_tmp, slen, NULL, 0);
buffer = (unsigned short*)malloc(length*2+1);
if(MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str_tmp, slen, (LPWSTR)buffer, length) > 0)
{
rtn = env->NewString((jchar*)buffer, length);
}
}
if(buffer)
{
free(buffer);
}
return rtn;
}
char* jstring互轉
JNIEXPORT jstring JNICALL Java_com_explorer_jni_SambaTreeNative_getDetailsBy
(JNIEnv *env, jobject jobj, jstring pc_server, jstring server_user, jstring server_passwd)
{
const char *pc = env->GetStringUTFChars(pc_server, NULL);
const char *user = env->GetStringUTFChars(server_user, NULL);
const char *passwd = env->GetStringUTFChars(server_passwd, NULL);
const char *details = smbtree::getPara(pc, user, passwd);
jstring jDetails = env->NewStringUTF(details);
return jDetails;
}
4、Android.mk、Application.mk
1、Android.mk
Android.mk文件是GNU Makefile的1小部份,它用來對Android程序進行編譯,Android.mk中的變量都是全局的,解析進程會被定義。
1個Android.mk文件可以編譯多個模塊,模塊包括:APK程序、JAVA庫、CC++利用程序、CC++靜態庫、CC++同享庫。
簡單實例以下:
LOCAL_PATH := $(call my-dir) #表示是當前文件的路徑
include $(CLEAR_VARS) #指定讓GNU MAKEFILE該腳本為你清除許多 LOCAL_XXX 變量
LOCAL_MODULE:= helloworld #標識你在 Android.mk 文件中描寫的每一個模塊
MY_SOURCES := foo.c #自定義變量
ifneq ($(MY_CONFIG_BAR),)
MY_SOURCES += bar.c
endif
LOCAL_SRC_FILES += $(MY_SOURCES) #包括將要編譯打包進模塊中的 C 或 C++源代碼文件
include $(BUILD_SHARED_LIBRARY) #根據LOCAL_XXX系列變量中的值,來編譯生成同享庫(動態鏈接庫)
GNU Make系統變量
變量 | 描寫 |
CLEAR_VARS | 指向1個編譯腳本,幾近所有未定義的 LOCAL_XXX 變量都在"Module-description"節中列出。必須在開始1個新模塊之前包括這個腳本:include$(CLEAR_VARS),用于重置除LOCAL_PATH變量外的,所有LOCAL_XXX系列變量。 |
BUILD_SHARED_LIBRARY | 指向編譯腳本,根據所有的在 LOCAL_XXX 變量把列出的源代碼文件編譯成1個同享庫。 |
BUILD_STATIC_LIBRARY | 1個 BUILD_SHARED_LIBRARY 變量用于編譯1個靜態庫。靜態庫不會復制到的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) ,在想要在真實的裝備中針對1個特別的目標系統進行測試時,會有用。在默許的情況下,它會是'android⑶-arm'。 |
| |
模塊描寫變量
變量 | 描寫 |
LOCAL_PATH | 這個變量用于給出當前文件的路徑。必須在 Android.mk 的開頭定義,可以這樣使用:LOCAL_PATH := $(call my-dir) 這個變量不會被$(CLEAR_VARS)清除,因此每 個 Android.mk 只需要定義1次(即便在1個文件中定義了幾個模塊的情況下)。 |
LOCAL_MODULE | 這是模塊的名字,它必須是唯1的,而且不能包括空格。必須在包括任1的$(BUILD_XXXX)腳本之前定義它。模塊的名字決定了生成文件的名字。例如,如果1個1個同享庫模塊的名字是,那末生成文件的名字就是 lib.so。但是,在的 NDK 生成文件中(或 Android.mk 或 Application.mk),應當只觸及(援用)有正常名字的其他模塊。 |
LOCAL_SRC_FILES | 這是要編譯的源代碼文件列表。只要列出要傳遞給編譯器的文件,由于編譯系統自動計算依賴。注意源代碼文件名稱都是相對 LOCAL_PATH的,你可使用路徑部份。 |
LOCAL_CPP_EXTENSION | 這是1個可選變量, 用來指定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 | 默許情況下, 在試圖編譯1個同享庫時,任何未定義的援用將致使1個“未定義的符號”毛病。 |
LOCAL_ARM_MODE | 默許情況下, arm目標2進制會以 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 | 返回1個位于當前'my-dir'路徑的子目錄中的所有Android.mk的列表。 |
this-makefile | 返回當前Makefile 的路徑(即這個函數調用的地方) |
parent-makefile | 返回調用樹中父 Makefile 路徑。即包括當前Makefile的Makefile 路徑。 |
grand-parent-makefile | 返回調用樹中父Makefile的父Makefile的路徑 |
| |
范例:
2、
編譯1個簡單的APK
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Build all java files in the java subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Name of the APK to build
LOCAL_PACKAGE_NAME := LocalPackage
# Tell it to build an APK
include $(BUILD_PACKAGE)
編譯1個依賴靜態.jar文件的APK
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# List of static libraries to include in the package
LOCAL_STATIC_JAVA_LIBRARIES := static-library
# Build all java files in the java subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Name of the APK to build
LOCAL_PACKAGE_NAME := LocalPackage
# Tell it to build an APK
include $(BUILD_PACKAGE)
注:LOCAL_STATIC_JAVA_LIBRARIES 后面應是你的APK程序所需要的JAVA庫的JAR文件名。
編譯1個需要platform key簽名的APK
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Build all java files in the java subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Name of the APK to build
LOCAL_PACKAGE_NAME := LocalPackage
LOCAL_CERTIFICATE := platform
# Tell it to build an APK
include $(BUILD_PACKAGE)
注:LOCAL_CERTIFICATE 后面應當是簽名文件的文件名
編譯1個需要特殊vendor key簽名的APK
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Build all java files in the java subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Name of the APK to build
LOCAL_PACKAGE_NAME := LocalPackage
LOCAL_CERTIFICATE := vendor/example/certs/app
# Tell it to build an APK
include $(BUILD_PACKAGE)
裝載1個普通的第3方APK
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Module name should match apk name to be installed.
LOCAL_MODULE := LocalModuleName
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := platform
include $(BUILD_PREBUILT)
裝載需要.so(動態庫)的第3方apk
LOCAL_PATH := $(my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := baiduinput_android_v1.1_1000e
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := platform
include $(BUILD_PREBUILT)
#################################################################
####### copy the library to /system/lib #########################
#################################################################
include $(CLEAR_VARS)
LOCAL_MODULE := libinputcore.so
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)
LOCAL_SRC_FILES := lib/$(LOCAL_MODULE)
OVERRIDE_BUILD_MODULE_PATH := $(TARGET_OUT_INTERMEDIATE_LIBRARIES)
include $(BUILD_PREBUILT)
編譯1個靜態java庫
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Build all java files in the java subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Any libraries that this library depends on
LOCAL_JAVA_LIBRARIES := android.test.runner
# The name of the jar file to create
LOCAL_MODULE := sample
# Build a static jar file.
include $(BUILD_STATIC_JAVA_LIBRARY)
注:LOCAL_JAVA_LIBRARIES表示生成的java庫的jar文件名。
編譯C/C++利用程序模板
LOCAL_
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
------分隔線----------------------------
------分隔線----------------------------