自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Android開發(fā)進(jìn)階:Android NDK介紹

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

導(dǎo)讀

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

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

什么是 Android NDK?

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

何時使用NDK?

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

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

什么是JNI?

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

JNI的優(yōu)勢

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

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

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

 

 

圖1 — JNI接口指針
圖1 — JNI接口指針

 

 

這張JNI函數(shù)表的組成就像C++的虛函數(shù)表。虛擬機(jī)可以運(yùn)行多張函數(shù)表,舉例來說,一張調(diào)試函數(shù)表,另一張是調(diào)用函數(shù)表。JNI接口指針僅在當(dāng)前線程中起作用。這意味著指針不能從一個線程進(jì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 — 一個接口指針。
  • obj — 在本地方法中聲明的對象引用。
  • i和s — 用于傳遞的參數(shù)。

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

局部引用和全局引用

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

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

全局引用在完全釋放之前都是有效的。要創(chuàng)建一個全局引用,需要調(diào)用NewGlobalRef方法。如果全局引用并不是必須的,可以通過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); 

錯誤

JNI不會檢查NullPointerException、IllegalArgumentException這樣的錯誤,原因是:

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

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

  1. jthrowable ExceptionOccurred(JNIEnv *env); 

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

JNI原始類型

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

Java類型

本地類型(JNI

描述

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

JNI引用類型

 

圖2 — JNI引用類型
圖2 — JNI引用類型

 

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

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

#p#

JNI函數(shù):

JNI接口不僅有自己的數(shù)據(jù)集(dataset)也有自己的函數(shù)?;仡欉@些數(shù)據(jù)集和函數(shù)需要花費(fèi)我們很多時間??梢詮墓俜轿臋n中找到更多信息:

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

JNI函數(shù)使用示例

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

  1. #include <jni.h> 
  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(); 

讓我們來逐個分析字符串:

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

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

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

線程

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

***步

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

 

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

 

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

要創(chuàng)建一個本地項目和一個Android項目可以參照以下面的步驟:

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

Android.mk

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

最精簡的配置示例:

  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) 

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

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

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

MY_SOURCE := NDKBegining.c

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

LOCAL_SRC_FILES += $(MY_SOURCE)

Application.mk

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

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

NDK-BUILDS

NDK-build是一個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=<file> — 指定Application.mk路徑。

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

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

如何建立一個項目?

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

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

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

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

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

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

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

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

考慮到文章長度和可讀性,我并沒有加入簡單的代碼示例,所以你在這里找不到。如果需要,請訪問這個鏈接https://github.com/viacheslavtitov/NDKBegining。

責(zé)任編輯:徐川 來源: ImportNew
相關(guān)推薦

2010-12-23 09:11:17

讀寫Android文件

2012-02-07 10:05:40

jQuery MobijQuery Mobi

2014-01-07 14:53:37

Android開發(fā)依賴注入Roboguice

2011-05-03 15:28:15

BlackBerryWidget

2011-10-18 10:17:39

Android應(yīng)用開發(fā)

2011-10-18 10:25:08

Android應(yīng)用開發(fā)

2013-01-16 15:07:30

Android NDKAndroid開發(fā)

2009-04-30 09:02:53

AcerAndroid開發(fā)

2013-01-16 14:41:14

Android開發(fā)Android SDK

2011-08-17 16:23:31

iPhone開發(fā)UIViewContr

2014-07-30 16:43:49

Android

2011-08-17 16:29:12

iPhone開發(fā)UIButton

2021-01-20 08:16:06

異步Dotnet Core多路徑

2011-05-31 15:09:02

Android SDK 官方下載

2010-08-11 10:58:06

AndroidAndroid NDK

2014-03-31 15:32:36

AndroidNDK開發(fā)

2013-01-10 13:50:25

Android開發(fā)組件

2010-01-25 15:35:08

Android NDK

2021-12-30 10:43:21

Android函數(shù)Crash

2019-07-09 09:48:41

前端開發(fā)技術(shù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號