日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線(xiàn)溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
Android開(kāi)發(fā)進(jìn)階:AndroidNDK介紹

導(dǎo)讀

創(chuàng)新互聯(lián)公司2013年成立,是專(zhuān)業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目網(wǎng)站建設(shè)、成都網(wǎng)站制作網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元祁連做網(wǎng)站,已為上家服務(wù),為祁連各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話(huà):18980820575

為了在Android OS系統(tǒng)上開(kāi)發(fā)應(yīng)用程序,Google提供了兩種開(kāi)發(fā)包:SDK和NDK。你可以從Google官方查閱到有許多關(guān)于SDK 的優(yōu)秀的書(shū)籍、文章作為參考,但Google沒(méi)有提供足夠的NDK資料。在現(xiàn)有的書(shū)籍中,我認(rèn)為Cinar O.寫(xiě)于2012年 的”P(pán)ro Android C++ with the NDK”值得一讀。

本文旨在幫助那些缺乏Android NDK經(jīng)驗(yàn)但又想擴(kuò)充這方面知識(shí)的人們。我所關(guān)注的是JNI(本地編程接口,簡(jiǎn)稱(chēng)JNI)。本文分上下兩篇,在上篇中,會(huì)從JNI為接口開(kāi)始講起;下篇會(huì)進(jìn)行回顧,并給出帶兩個(gè)文件讀寫(xiě)功能的實(shí)例。

什么是 Android NDK?

Android NDK(Native Development Kit )是一套工具集合,允許你用像C/C++語(yǔ)言那樣實(shí)現(xiàn)應(yīng)用程序的一部分。

何時(shí)使用NDK?

Google僅在極少數(shù)情況下建議使用NDK,有如下使用場(chǎng)景:

  • 必須提高性能(例如,對(duì)大量數(shù)據(jù)進(jìn)行排序)。
  • 使用第三方庫(kù)。舉例說(shuō)明:許多第三方庫(kù)由C/C++語(yǔ)言編寫(xiě),而Android應(yīng)用程序需要使用現(xiàn)有的第三方庫(kù),如Ffmpeg、OpenCV這樣的庫(kù)。
  • 底層程序設(shè)計(jì)(例如,應(yīng)用程序不依賴(lài)Dalvik Java虛擬機(jī))。

什么是JNI?

JNI是一種在Java虛擬機(jī)控制下執(zhí)行代碼的標(biāo)準(zhǔn)機(jī)制。代碼被編寫(xiě)成匯編程序或者C/C++程序,并組裝為動(dòng)態(tài)庫(kù)。也就允許了非靜態(tài)綁定用法。這提供了一個(gè)在Java平臺(tái)上調(diào)用C/C++的一種途徑,反之亦然。

JNI的優(yōu)勢(shì)

與其他類(lèi)似接口(Netscape Java運(yùn)行接口、Microsoft的原始本地接口、COM/Java接口)相比,JNI主要的競(jìng)爭(zhēng)優(yōu)勢(shì)在于:它在設(shè)計(jì)之初就確保了二進(jìn)制的兼容 性,JNI編寫(xiě)的應(yīng)用程序兼容性以及在某些具體平臺(tái)上的Java虛擬機(jī)兼容性(當(dāng)談及JNI,這里并不特別針對(duì)Dalvik;JNI由Oracle開(kāi)發(fā), 適用于所有Java虛擬機(jī))。這就是為什么C/C++編譯后的代碼無(wú)論在任何平臺(tái)上都能執(zhí)行。不過(guò),一些早期版本并不支持二進(jìn)制兼容。

二進(jìn)制兼容性是一種程序兼容性類(lèi)型,允許一個(gè)程序在不改變其可執(zhí)行文件的條件下在不同的編譯環(huán)境中工作。

JNI組織結(jié)構(gòu)

 

 

圖1 — JNI接口指針

 

 

這張JNI函數(shù)表的組成就像C++的虛函數(shù)表。虛擬機(jī)可以運(yùn)行多張函數(shù)表,舉例來(lái)說(shuō),一張調(diào)試函數(shù)表,另一張是調(diào)用函數(shù)表。JNI接口指針僅在當(dāng)前線(xiàn)程中起作用。這意味著指針不能從一個(gè)線(xiàn)程進(jìn)入另一個(gè)線(xiàn)程。然而,可以在不同的線(xiàn)程中調(diào)用本地方法。

示例代碼:

 
 
 
 
  1. jdouble Java_pkg_Cls_f__ILjava_lang_String_2 (JNIEnv *env, jobject obj, jint i, jstring s) 
  2.      const char *str = (*env)->GetStringUTFChars(env, s, 0); 
  3.      (*env)->ReleaseStringUTFChars(env, s, str); 
  4.      return 10; 
  • *env — 一個(gè)接口指針。
  • obj — 在本地方法中聲明的對(duì)象引用。
  • i和s — 用于傳遞的參數(shù)。

原始類(lèi)型(Primitive Type)在虛擬機(jī)和本機(jī)代碼進(jìn)行拷貝,對(duì)象之間使用引用進(jìn)行傳遞。VM(虛擬機(jī))要追蹤所有傳遞給本地代碼的對(duì)象引用。GC無(wú)法釋放所有傳遞給本地代碼的對(duì)象引用。與此同時(shí),本機(jī)代碼應(yīng)該通知VM不需要的對(duì)象引用。

局部引用和全局引用

JNI定義了三種引用類(lèi)型:局部引用、全局引用和全局弱引用。局部引用在方法完成之前是有效的。所有通過(guò)JNI函數(shù)返回的Java對(duì)象都是本地引 用。程序員希望VM會(huì)清空所有的局部引用,然而局部引用僅在其創(chuàng)建的線(xiàn)程里可用。如果有必要,局部引用可以通過(guò)接口中的DeleteLocalRef JNI方法立即釋放:

 
 
 
 
  1. jclass clazz; 
  2. clazz = (*env)->FindClass(env, "java/lang/String"); 
  3. ... 
  4. (*env)->DeleteLocalRef(env, clazz) 

全局引用在完全釋放之前都是有效的。要?jiǎng)?chuàng)建一個(gè)全局引用,需要調(diào)用NewGlobalRef方法。如果全局引用并不是必須的,可以通過(guò)DeleteGlobalRef方法刪除:

 
 
 
 
  1. jclass localClazz; 
  2. jclass globalClazz; 
  3. ... 
  4. localClazz = (*env)->FindClass(env, "java/lang/String"); 
  5. globalClazz = (*env)->NewGlobalRef(env, localClazz); 
  6. ... 
  7. (*env)->DeleteLocalRef(env, localClazz); 

錯(cuò)誤

JNI不會(huì)檢查NullPointerException、IllegalArgumentException這樣的錯(cuò)誤,原因是:

  • 導(dǎo)致性能下降。
  • 在絕大多數(shù)C的庫(kù)函數(shù)中,很難避免錯(cuò)誤發(fā)生。

JNI允許用戶(hù)使用Java異常處理。大部分JNI方法會(huì)返回錯(cuò)誤代碼但本身并不會(huì)報(bào)出異常。因此,很有必要在代碼本身進(jìn)行處理,將異常拋給Java。在JNI內(nèi)部,首先會(huì)檢查調(diào)用函數(shù)返回的錯(cuò)誤代碼,之后會(huì)調(diào)用ExpectOccurred()返回一個(gè)錯(cuò)誤對(duì)象。

 
 
 
 
  1. jthrowable ExceptionOccurred(JNIEnv *env); 

例如:一些操作數(shù)組的JNI函數(shù)不會(huì)報(bào)錯(cuò),因此可以調(diào)用ArrayIndexOutofBoundsException或ArrayStoreExpection方法報(bào)告異常。

JNI原始類(lèi)型

JNI有自己的原始數(shù)據(jù)類(lèi)型和數(shù)據(jù)引用類(lèi)型。

Java類(lèi)型

本地類(lèi)型(JNI)

描述

boolean(布爾型) jboolean 無(wú)符號(hào)8個(gè)比特
byte(字節(jié)型) jbyte 有符號(hào)8個(gè)比特
char(字符型) jchar 無(wú)符號(hào)16個(gè)比特
short(短整型) jshort 有符號(hào)16個(gè)比特
int(整型) jint 有符號(hào)32個(gè)比特
long(長(zhǎng)整型) jlong 有符號(hào)64個(gè)比特
float(浮點(diǎn)型) jfloat 32個(gè)比特
double(雙精度浮點(diǎn)型) jdouble 64個(gè)比特
void(空型) void N/A

JNI引用類(lèi)型

 

圖2 — JNI引用類(lèi)型

 

改進(jìn)的UTF-8編碼

JNI使用改進(jìn)的UTF-8字符串來(lái)表示不同的字符類(lèi)型。Java使用UTF-16編碼。UTF-8編碼主要使用于C語(yǔ)言,因?yàn)樗木幋a用\u000表示為0xc0,而不是通常的0×00。非空ASCII字符改進(jìn)后的字符串編碼中可以用一個(gè)字節(jié)表示。

#p#

JNI函數(shù):

JNI接口不僅有自己的數(shù)據(jù)集(dataset)也有自己的函數(shù)。回顧這些數(shù)據(jù)集和函數(shù)需要花費(fèi)我們很多時(shí)間。可以從官方文檔中找到更多信息:

http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/functions.html

JNI函數(shù)使用示例

下面會(huì)通過(guò)一個(gè)簡(jiǎn)短的示例確保你對(duì)這些資料所講的內(nèi)容有了正確的理解:

 
 
 
 
  1. #include  
  2.     ... 
  3. JavaVM *jvm; 
  4. JNIEnv *env; 
  5. JavaVMInitArgs vm_args; 
  6. JavaVMOption* options = new JavaVMOption[1]; 
  7. options[0].optionString = "-Djava.class.path=/usr/lib/java"; 
  8. vm_args.version = JNI_VERSION_1_6; 
  9. vm_args.nOptions = 1; 
  10. vm_args.options = options; 
  11. vm_args.ignoreUnrecognized = false; 
  12. JNI_CreateJavaVM(&jvm, &env, &vm_args); 
  13. delete options; 
  14. jclass cls = env->FindClass("Main"); 
  15. jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V"); 
  16. env->CallStaticVoidMethod(cls, mid, 100); 
  17. jvm->DestroyJavaVM(); 

讓我們來(lái)逐個(gè)分析字符串:

  • JavaVM — 提供了一個(gè)接口,可以調(diào)用函數(shù)創(chuàng)建、刪除Java虛擬機(jī)。
  • JNIEnv — 確保了大多數(shù)的JNI函數(shù)。
  • JavaVMlnitArgs —  Java虛擬機(jī)參數(shù)。
  • JavaVMOption — Java虛擬機(jī)選項(xiàng)。

JNI的_CreateJavaVM()方法初始化Java虛擬機(jī)并向JNI接口返回一個(gè)指針。

JNI_DestroyJavaVM()方法可以載入創(chuàng)建好的Java虛擬機(jī)。

線(xiàn)程

內(nèi)核負(fù)責(zé)管理所有在Linux上運(yùn)行的線(xiàn)程;線(xiàn)程通過(guò)AttachCurrentThread和AttachCurrentThreadAsDaemon函數(shù)附加到Java虛擬機(jī)。如果線(xiàn)程沒(méi)有被添加成功,則不能訪(fǎng)問(wèn)JNIEnv。 Android系統(tǒng)不能停止JNI創(chuàng)建的線(xiàn)程,即使GC(Garbage Collection)在運(yùn)行釋放內(nèi)存時(shí)也不行。直到調(diào)用DetachCurrentThread方法,該線(xiàn)程才會(huì)從Java虛擬機(jī)脫離。

***步

你的項(xiàng)目結(jié)構(gòu)應(yīng)該如圖3所示:

 

圖3—工程結(jié)構(gòu)

 

在圖3中,所有本地代碼都存儲(chǔ)到一個(gè)jni的文件夾。在新建一個(gè)工程后,Libs文件夾會(huì)被分為四個(gè)子文件夾。這意味著一個(gè)子目錄對(duì)應(yīng)一種處理器架構(gòu),庫(kù)的數(shù)量取決于處理器架構(gòu)的數(shù)量。

要?jiǎng)?chuàng)建一個(gè)本地項(xiàng)目和一個(gè)Android項(xiàng)目可以參照以下面的步驟:

  • 創(chuàng)建一個(gè)jni文件夾 — 包含本地代碼的項(xiàng)目源代碼根目錄。
  • 創(chuàng)建一個(gè)Android.mk文件用來(lái)構(gòu)建項(xiàng)目。
  • 創(chuàng)建一個(gè)Application.mk文件用來(lái)存儲(chǔ)編譯參數(shù)。雖然這不是必須的配置,但是推薦你這么做。這樣會(huì)使得編譯設(shè)置更加靈活。
  • 創(chuàng)建一個(gè)ndk-build文件以此來(lái)顯示編譯過(guò)程(同樣這一步也不是必須的)。

Android.mk

就像前面提到的,Android.mk是編譯本地項(xiàng)目的makefile。Android.mk把代碼按照模塊進(jìn)行了劃分,把靜態(tài)庫(kù)(static library)拷貝到項(xiàng)目的libs文件夾,生成共享庫(kù)(shared library)和獨(dú)立的可執(zhí)行文件。

最精簡(jiǎn)的配置示例:

 
 
 
 
  1. LOCAL_PATH := $(call my-dir) 
  2. include $(CLEAR_VARS) 
  3. LOCAL_MODULE    := NDKBegining 
  4. LOCAL_SRC_FILES := ndkBegining.c 
  5. include $(BUILD_SHARED_LIBRARY) 

讓我們來(lái)仔細(xì)看看:

  • LOCAL_PATH:-$(call my-dir)  — 調(diào)用函數(shù)宏my-dir返回當(dāng)前文件所在路徑。
  • include $(CLEAR_VARS) — 清除所有LOCAL_PATH以外的變量。這是必須的步驟,考慮到所有編譯控制文件都位于同一個(gè)GNU MAKE執(zhí)行環(huán)境中,所有變量都是全局的。
  • LOCAL_MODULE — 輸出模塊名稱(chēng)。在上述例子中,輸出模塊叫做NDKBegining。但是在生成以后,會(huì)在libs文件夾中創(chuàng)建libNDKbegining庫(kù)。同 時(shí),Android系統(tǒng)會(huì)為其添加一個(gè)前綴名lib,例如一個(gè)被命名為”foo”的共享庫(kù)模塊,將會(huì)生成”libfoo.so”文件。 但是在Java代 碼中使用庫(kù)時(shí)應(yīng)該忽略前綴名(也就是說(shuō),名稱(chēng)應(yīng)該和makefile一樣)。
  • LOCAL_SRC_FILE — 列出編譯所需要的源文件。
  • include $(BUILD_SHARED_LIBARY) — 輸出模塊的類(lèi)型。

你可以在Android.mk文件中設(shè)置自定義變量;但是必須遵守語(yǔ)法命名規(guī)則:LOCAL_、PRIVATE_、NDK_、APP_、my-dir。Google建議自定義示例前綴使用MY_,例如:

MY_SOURCE := NDKBegining.c

這樣就調(diào)用了一個(gè)變量$(MY_SOURCE)。變量同樣也可以被連接起來(lái),例如:

LOCAL_SRC_FILES += $(MY_SOURCE)

Application.mk

這個(gè)makefile中定義了好幾種變量讓編譯更加靈活:

  • APP_OPTM — 這個(gè)變量是可選的,用于指定程序是“release”還是“debug”。在構(gòu)建應(yīng)用程序模塊時(shí),該變量用來(lái)優(yōu)化構(gòu)建過(guò)程。你可以在調(diào)試中指定“release”,不過(guò)“debug”支持的配置選項(xiàng)更多。
  • APP_BUILD_SCRI為Android.mk定義了另一條路徑。
  • APP_ABI — 最重要的變量之一。它指定了編譯模塊時(shí)使用的目標(biāo)處理器架構(gòu)。默認(rèn)情況下,APP_ABI會(huì)設(shè)置為“armeabi”,對(duì)應(yīng)于ARMv5TE架構(gòu)。例如, 如果要支持 ARMv7,就需要設(shè)置為“armeabi-v7a”。對(duì)于IA-32-x86和MIPS-mips這樣支持多體系架構(gòu)的系統(tǒng),應(yīng)該把 APP_ABI設(shè)置為“armeabi armeabi-v7a x86 mips”。在NDK修訂版本7或更高的版本中,可以簡(jiǎn)單的設(shè)置APP_ABI := “all rather enumerating all the architectures”。
  • APP_PLATFORM — 為目標(biāo)平臺(tái)名稱(chēng);
  • APP_STL — Android提供了一個(gè)最精簡(jiǎn)的libstdc c++運(yùn)行庫(kù),因此開(kāi)發(fā)人員使用的c++功能是非常有限的。然而使用APP_STL變量就可以使這些庫(kù)支持?jǐn)U展功能。
  • NDK_TOOLCHAIN_VERSION-GCC — 選擇的GCC編譯器版本(默認(rèn)情況下設(shè)置為4.6)。

NDK-BUILDS

NDK-build是一個(gè)GNU Make的包裝容器。在NDK 4以后,ndk-build支持以下參數(shù):

  • clean — 清除所有已生成的二進(jìn)制文件。
  • NDK_DEBUG=1 — 生成可調(diào)式的代碼。
  • NDK_LOG=1 — 顯示日志信息(用于調(diào)試)。
  • NDK_HOST_32BIT=1 — 使Android系統(tǒng)支持64位版本(例如,NDK_PATH\toolchains\mipsel-linux-android-4.8\prebuilt\windows-x86_64,等等)。
  • NDK_APPLICATION_MK= — 指定Application.mk路徑。

在 NDK v5中,引入了NDK_DEBUG。當(dāng)NDK_DEBUG設(shè)置為“1”時(shí),便會(huì)生成可調(diào)試版本。如果沒(méi)有設(shè)置NDK_DEBUG,ndk-build會(huì)默 認(rèn)驗(yàn)證是否有在AndroidMainfest.xml文件中設(shè)置 android:debuggable=“true” 屬性。如果你使用的是NDK v8以后的版本,Google不建議你在AndoirdMainfest.xml文件中使用 android:debuggable 屬性(當(dāng)你使用“ant debug”或ADT插件生成調(diào)試版本時(shí),會(huì)自動(dòng)添加“NDK_DEBUG=1”)。

默認(rèn)情況下,設(shè)置了支持64位版本。你也可以通過(guò)設(shè)置“NDK_HOST_32BIT=1”強(qiáng)制使用一個(gè)32位的工具鏈來(lái)使用32位應(yīng)用程序。不過(guò),谷歌仍建議使用64位的應(yīng)用程序來(lái)提升大型程序的性能。

如何建立一個(gè)項(xiàng)目?

這 是個(gè)令人頭疼的步驟。你要安裝CDT插件并下載cygwin或mingw編譯器和Android NDK,在Eclipse設(shè)置里配置這些東西,但***還 是不能運(yùn)行。我***次開(kāi)始使用Android NDK時(shí),配置這些東西花了我3天時(shí)間。***發(fā)現(xiàn)問(wèn)題出在Cygwin編譯器身上:應(yīng)該為項(xiàng)目文件夾設(shè)置讀、寫(xiě)、可執(zhí)行的所有權(quán)限。

現(xiàn)在可就簡(jiǎn)單多咯!只需要照著這個(gè)鏈接到網(wǎng)址:http://developer.android.com/sdk/index.html 下載ADT包,這里面有開(kāi)始編譯環(huán)節(jié)需要用到的所有東西。

從Java代碼中調(diào)用本地方法

要從Java中調(diào)用本地代碼,首先你要在Java類(lèi)中定義本地方法。例如:

 
 
 
 
  1. native String nativeGetStringFromFile(String path) throws IOException; 
  2. native void nativeWriteByteArrayToFile(String path, byte[] b) throws IOException 

你得在方法前使用“native”關(guān)鍵字。,這樣編譯器就知道這是JNI的入口點(diǎn)。這些方法會(huì)在C/C++文件中實(shí)現(xiàn)。Google建議用 “native+x”這樣的命名方式,“x”代表著方法的實(shí)際名稱(chēng)。還有,在實(shí)現(xiàn)這些方法前你還得手動(dòng)生成一個(gè)頭文件。你可以手動(dòng)執(zhí)行此操作或者使用 JDK的 javah工具生成頭文件。然后讓我們將進(jìn)一步探討如何不用控制臺(tái),直接使用標(biāo)準(zhǔn)的Eclipse開(kāi)發(fā)環(huán)境:

  • 打開(kāi)Eclipse,選擇Run -> External-tool-External -> External tools configurations。
  • 新建配置。
  • 指定javah.exe在jdk里的絕對(duì)路徑(例如,C:\Program Files (x86)\Java\jdk1.6.0_35\bin\javah.exe)。
  • 在工作目錄中指定bin/class目錄的路徑(例如,?${workspace_loc:/NDKBegin/bin/classes}?)。
  • 填入如下參數(shù):“-jni ${java_type_name}” (注意,輸入時(shí)不需要帶引號(hào))。

現(xiàn)在你可以運(yùn)行了。你的頭文件應(yīng)該放在bin/classes目錄下。下一步,復(fù)制這些文件到本地工程的jni目錄。打開(kāi)工程的配置菜單并選擇 Andorid Tools這一項(xiàng) — 添加本地庫(kù)(Add Native Library)。這樣我們就可以使用jni.h頭文件中包含的函數(shù)了。在此之后,你還要?jiǎng)?chuàng)建一個(gè).cpp的文件(有時(shí)候 Eclipse會(huì)默認(rèn)生成),并且方法實(shí)現(xiàn)已經(jīng)在頭文件中定義。

考慮到文章長(zhǎng)度和可讀性,我并沒(méi)有加入簡(jiǎn)單的代碼示例,所以你在這里找不到。如果需要,請(qǐng)?jiān)L問(wèn)這個(gè)鏈接https://github.com/viacheslavtitov/NDKBegining。


當(dāng)前題目:Android開(kāi)發(fā)進(jìn)階:AndroidNDK介紹
文章網(wǎng)址:http://m.5511xx.com/article/dphissh.html