NDK Android* 應用移植方法

概述

本指南用於幫助開發者將現有的基於 ARM* 的 NDK 應用移植到 x86。假設您已經擁有一個正常執行的應用,需要知道怎樣能夠高速讓 x86 設備在 Android* Market 中找到您的應用,本文將能夠爲您提供一些入門信息。同一時候本指南還提供了一些技巧和指南,以幫助您解決在移植過程當中可能會遇到的編譯器問題。php

內容

  1. 概述
  2. NDK 概述
  3. 移植概述
  4. 移植技巧:從 ARM* 到 x86
  5. 結論

 

NDK 概述

原生開發套件 NDK 是一款強大的工具,將原生 x86 代碼的強大功能和 Android* 應用的圖形界面結合在一塊兒。經過使用該工具,開發者將能夠提高某些應用的性能優點。但同一時候開發者也需要十分慎重,因爲在某些狀況並不能實現預期的效果.html

NDK 致力於支持開發者完畢下面工做:java

  • 編譯原生 C/C++ 庫(由包裝程序 Java* 代碼調用)。以備在 Android* 應用包中使用
  • 又一次編譯 ARM* 原生庫,供在 x86 平臺(英特爾® 凌動™ 微架構)上使用。並在必要時進行移植

對於以上提到的第二點。部分狀況下可能僅僅需要簡單地改動 Build Flag 並又一次編譯就能夠,但有時卻並不這麼簡單。linux

好比。假設原生庫涉及到 C 代碼內的內嵌彙編,那麼代碼將沒法經過簡單的彙編來實現在兩種不一樣的架構自如執行的目標,此時將需要進行又一次編寫(不少其它信息請參見比較 ARM* 的 NEON* 與英特爾的 SSE 的部分)。android

JNI 的性能影響和開銷c++

Java* 原生接口(JNI)將 Android* Java* 代碼與由 NDK 預編譯的原生代碼結合在一塊兒。編程

如欲瞭解有關該接口的不少其它信息,請訪問: http://java.sun.com/docs/books/jni/架構

以上連接對 JNI 規範進行了普遍、深刻的剖析。 若僅僅是但願瞭解其概況。Wiki 頁面就能夠知足您的要求(存在疑問時,請隨時參考規範以檢查其正確性)。Wiki 頁面的網址例如如下: http://en.wikipedia.org/wiki/Java_Native_Interfaceapp

JNI 的開銷十分龐大。所以在理想狀況下,開發者應在應用中儘量下降對 JNI 的調用。ide

詳細而言,在 Android* 應用中使用原生代碼並不必定能提高性能。一般而言,當原生代碼涉及到由 CPU 進行的運算時(如大量使用 SSE 指令),將可以實現必定的性能提高。

但在另一些狀況下,如現有的應用僅用於爲用戶提供複雜的 Web 界面。此時經過 JNI 使用原生代碼可能會下降性能。

在什麼時候該用和不應用 NDK 上。不存在成文的規則,以上幾點僅僅是提供了一些需要注意的通用準則和事項。

獲取 NDK

開發者可經過下面網址獲取 NDK 的最新版本號: http://developer.android.com/sdk/ndk/index.html。 在最新版本號 NDK r6b 中。NDK 可用於構建基於 ARM* 和基於 x86(英特爾® 凌動™ 微架構)的原生庫。 這爲開發者在一個應用包內進行原生代碼移植提供了方便。

NDK 內部組件簡單介紹 Makefiles

開發者將需要爲項目建立一個 Android.mk 文件和一個 Application.mk 文件(可選)。當中。Application.mk 文件用於描寫敘述您的應用需要哪些原生模塊。

Android.mk 文件用於控制怎樣和從哪裏構建一個模塊(靜態/共享庫)。下面是一個簡單 Android.mk 文件的片斷:


圖 1:簡單 Android.mk 文件的內容

 

構建系統時將前置一個庫,並同一時候生成一個名稱爲 libtest.so 的庫。

按預期。開發人員將在 LOCAL_SRC_FILES 中爲項目源文件命名。 LOCAL_LDLIBS 和 LOCAL_CFLAGS 分別用於指定 Linking Flag(連接標記)和Compilation Flag(編譯標記)。

命令行構建

下面命令行提供了一個有關怎樣指定構建指向 x86 架構的演示樣例:ndk-build APP_ABI=x86

調用原生庫可採用下面兩種方法: System.loadLibrary("relative_path_and_name") and System.load("full_path_to_lib_file")。 前者更常用,後者更穩定。 使用前者時,Android.mk 指定的庫名稱中的「lib」部分可丟棄。 調用示比例如如下:


圖 2:調用原生代碼演示樣例

 

此外,對於原生代碼,開發者需要確保原生代碼的輸入方法具備正確的 JNIEXPORT 方法簽名。而不是典型的 C/C++ 標頭。前面說起的 JNI 連接包括有不少其它相關信息。

處理原生庫文件

開發者可經過兩種方式載入原生庫:1)在 Android* apk 包中提供該庫並在執行時對其進行引用;2)在 Android* 文件系統上提供通往該庫的絕對路徑。

採用以上方式中的哪種取決於開發者的偏好。

但無論採用哪一種方式,均應進行對應的正確處理。

執行時驗證

經過使用 adb logcat 命令。開發者可確保在執行時成功載入目標原生庫。

下面提供了一個描寫敘述原生庫已載入的系統日誌的演示樣例。注意需提供通往原生庫文件的完整路徑。


圖 2:調用原生代碼演示樣例

總結

以上各部分提供了有關怎樣使用 NDK 的入門知識。

如欲瞭解更加複雜的細節,請閱讀 NDK 應用包中包括的相關文檔。 這些文檔提供有出色的教程和針對各類應用的源碼演示樣例。

移植概述

對於大多數應用而言。將現有的 NDK 應用移植到 x86 很easy。

除非原生代碼使用 ARM* 特有的特性。不然移植應用僅僅需進行又一次編譯、又一次打包和又一次公佈操做就能夠完畢。

下面內容向您介紹了將 NDK 應用移植到 x86 涉及到的步驟。

  1. 獲取最新的 NDK 工具。

    x86 支持最早在 android-ndk-r6 中提供,但當時仍存在一些問題,以後谷歌很是快進行了修復。確保您已經從 Android* NDK 站點 下載和安裝了最新的(寫入時,最新的 NDK 爲 android-ndk-r6b)NDK。

     

  2. 假設您已有一個 Application.mk 文件,可編輯 APP_ABI 行增長 x86。演示樣例:

    APP_ABI := armeabi armeabi-v7a x86

    假設您沒有 Application.mk 文件,可將 x86 加入到命令行構件中。下面爲構建一個 NDK 演示樣例應用時的命令行和輸出內容。
    $ ndk-build APP_ABI="armeabi armeabi-v7a x86"

    Install : test-libstl => libs/armeabi/test-libstl
    Install : test-libstl => libs/armeabi-v7a/test-libstl
    Install : test-libstl => libs/x86/test-libstl
  3. 在前一步中,咱們可以發現包括每種架構的二進制代碼的目錄在 Libs 目錄下建立。下一步,咱們將又一次打包 APK 以包括新庫。由於 Libs 目錄位於根項目目錄之下,用於建立 APK 的構建工具已經知曉該目錄中的二進制代碼。利用 Eclipse。僅僅需簡單地又一次構建項目 APK 就能夠包括新的 x86 二進制代碼。

    使用命令行進行構建的操做與此一樣。下面列出了又一次構建演示樣例演示 hello-jni 時的演示樣例輸出內容:

    $ android.bat update project --path C:/Tools/android-ndk-r6b/samples/hello-jni
    Updated local.properties
    Added file C:\Tools\android-ndk-r6b\samples\hello-jni\build.xml
    Added file C:\Tools\android-ndk-r6b\samples\hello-jni\proguard.cfg
    $ ant -f hello-jni/build.xml debug
    Buildfile: C:\Tools\android-ndk-r6b\samples\hello-jni\build.xml

    debug:
    [echo] Running zip align on final apk...
    [echo] Debug Package: android-ndk-r6b\samples\hello-jni\bin\HelloJni-debug.apk
    BUILD SUCCESSFUL

  4. 下一步爲在英特爾架構設備或 x86 模擬器上進行執行和測試。

    驗證所有二進制代碼均已正確打包的最後一步爲使用 zip 存檔工具打開 APK 並確保當中包括二進制代碼。下面是存在 x86 二進制代碼時 APK 結構外觀的截屏。

移植技巧:從 ARM* 到 x86

將應用移植到 x86 應當很是easy,雖然很是多人可能都有這種想法,但在實際的代碼中。仍需要慎重注意並解決英特爾® 凌動™ 和 ARM* 架構之間的差別。下面主題介紹了您在移植過程當中可能遇到的問題以及怎樣予以解決。

工具鏈兼容性

您的構建環境有可能直接使用了工具鏈,而不是 Android* 構建腳本。在 ARM* 中,所用的路徑例如如下:

android-ndk\toolchains\arm-linux-androideabi-4.4.3
對於 x86。使用路徑:
android-ndk\toolchains\x86-4.4.3
有關具體信息。請參閱位於 android-ndk/docs/STANDALONE-TOOLCHAIN.html 的 NDK 文檔。

內存對齊影響:比較 ARM* 與英特爾® 凌動™

在 ARM* 和英特爾® 凌動™ 微架構之間移植 C/C++ 代碼時可能出現內存對齊不匹配的狀況。下面文章針對這一點提供了一個典型的演示樣例: /en-us/blogs/2011/08/18/understanding-x86-vs-arm-memory-alignment-on-android。基本的一點是:開發人員應當在設計代碼時,在必要的地方考慮對數據進行明白的強制對齊。不然,開發人員將沒法保證數據在不一樣的平臺能夠獲得正確的處理。

浮點預算:比較 ARM 與英特爾® 凌動™

眼下,在構建 NDK 庫時,可以使用三種支持的應用二進制接口(ABI):

  1. ‘armeabi’ – 默認選項,將建立以基於 ARM* v5TE 的設備爲目標的庫。具備這樣的目標的浮點運算使用軟件浮點運算。

    使用此 ABI 建立的二進制代碼將可以在所有 ARM* 設備上執行。

  2. ‘armeabi-v7a’ – 建立支持基於 ARM* v7 的設備的庫,並將使用硬件 FPU 指令。

  3. ‘x86’ – 生成的二進制代碼可支持包括基於硬件的浮點運算的 IA-32 指令集。

所有這些 ABI 選項均支持浮點運算。除非使用的是特定於 ARM* 的彙編指令,不然在將代碼移植到 x86 時不會發生故障。其優點在於,假設碰巧您的應用僅針對「armeabi」進行編譯,而現在需要支持 x86,則您在進行大多數浮點運算時均能感受到性能提高。

將 ARM* NEON* 指令移植至英特爾® 凌動™ 的英特爾® SSE

雖然這篇短小的文章不可能一應俱全,但是下面提供的信息將可讓您大體瞭解在英特爾架構和 ARM* 中。SIMD 擴展的實施有何不一樣。藉助此簡單介紹,開發人員還將得到一些工具,以便於開始進行一些簡單的編碼練習。

什麼是 NEON?

NEON* 是一種 ARM* 技術,主要用於多媒體(智能手機和高清電視等)應用。ARM* 表示其基於 128 位 SIMD 引擎的技術 – ARM* Cortex*(一種串行擴展)—可提供比 ARM* v5 架構至少高 3 倍的性能,以及比 ARM* v6 至少高 2 倍的性能。如欲瞭解有關此技術的具體信息。以深刻了解 NEON 及其餘性能考慮,請訪問下面網址:http://www.arm.com/products/processors/technologies/neon.php

此處的關鍵理念爲,各寄存器被「堆積」成一個矢量。當中每一個寄存器均爲一個元素,並與其餘元素的數據類型相匹配。在此基礎之上。運算在管道內運行。於是這一方法被稱做 Packed SIMD。

SSE:英特爾推出的相似工具

SSE 指面向英特爾架構(IA)的SIMD 流指令擴展。眼下,英特爾® 凌動™ 最高支持 SSSE3(補充 SIMD 流指令擴展 3)。

凌動™ 暫不支持 SSE4.x。

後者也是一個 128 位引擎,用於打包浮點數據。這一運行模式開始於 MMX 技術。

SSx 是較新的技術,代替了 MMX。如欲瞭解具體信息,請參閱英特爾《IA-32 和 IA-64 軟件開發者手冊》中的「第一卷: 基礎架構」部分。網址爲: http://www.intel.com/content/www/cn/zh/processors/architectures-software-developer-manuals.html

眼下,SSE 概述部分在 5.5 節。

它提供 SSE、SSE二、SSE3 和 SSSE3 的操做碼。注意。數據運算通常會涉及處處理基於精度的打包浮點數值;並且需要在 XMM 寄存器之間。或在這些寄存器與內存之間批量數據傳輸。XMM 寄存器主要用於代替 MMX 寄存器。

NEON 與 SSE 在彙編層面的比較

在推薦使用前述《英特爾架構軟件開發者手冊》來了解所有單個 SSE(x) 助記符的同一時候,咱們也鼓舞開發者經過下面連接瞭解各類 SSE 彙編級指令。網址爲: http://neilkemp.us/src/sse_tutorial/sse_tutorial.html

在該連接中。您可經過「文件夾」部分直接跳到代碼演示樣例或首先具體瞭解某些背景信息。相同,下面直接來自 ARM* 的手冊提供了一些信息和 NEON* 彙編小片斷:/sites/default/files/m/b/4/c/DHT0002A_introducing_neon.pdf

請參閱 ARM* 文檔中的第 1.4 節。

下面是在通常層面上比較 NEON 和 SSE 彙編代碼時的幾個要點(注意:隨着技術的發展,信息隨時會過期。依據詳細的 SIMD 技術和當下的應用編碼問題。可能還存在其餘差別):

字節存儲次序。英特爾僅支持低位優先彙編,而 ARM* 則同一時候支持高位或低位優先順序(ARM* 支持兩種順序)。在提供的代碼演示樣例中,同英特爾同樣。ARM* 代碼採用的也是低位優先順序。

注意雖然如此,在 ARM 中可能會存在一些編譯器影響*。好比,使用 GCC* 爲 ARM* 進行編譯時具備 -mlittle-endian 和 -mbig-endian 標記。有關具體信息請訪問:http://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html

Granularity. 在引用簡單彙編代碼演示樣例的狀況中(請再次注意這並不包括開發者可能發現的 NEON 和 SSE 之間的所有差別),將 SSE 的 ADDPS 指令與 NEON 的 VADD.ix(即:x = 8 或 16)進行比較。

請注意,後者在做爲引用助記符的一部分而待處理的數據上有必定粒度減小。

NEON 與 SSE 在C/C++ 層面的比較

在將 C/C++ 代碼 NEON 代碼移植到 SSE 時,可能會出現很是多 API 問題。

此處請注意本文的一個若是。即這裏未使用內嵌彙編。而使用的是真正的 C/C++ 代碼。

對於更高層級的編程。NEON 與 SSE 之間的差別涉及到大尺寸數據(128 位)的處理。

本文針對這樣的移植練習提供了一個簡短演示樣例: http://stackoverflow.com/questions/7203231/neon-vs-intel-sse-equivalence-of-certain-operations

結論

咱們但願這一指南能夠爲您提供一些有用信息,幫助您成功將基於 NDK 的應用移植到 x86。移植到 x86 後。您的應用將能夠供一種全新類型的 Android* 設備進行下載、購買和使用。假設您在移植過程當中遇到問題,請隨時在本文中發表評論。

咱們將很樂意回答您的問題,爲您提供幫助。

相關文章與資源:

聲明

* 其它的名稱和品牌多是其它所有者的資產

英特爾公司 © 2011 年版權所有。 所有權保留。

英特爾、Atom 和凌動是英特爾在美國和/或其它國家的商標。

本文件裏包含關於英特爾產品的信息。 本文件不構成對不論什麼知識產權的受權,包含明示的、暗示的,也無論是基於禁止反言的原則或其它。

英特爾不承擔不論什麼其它責任。英特爾在此做出免責聲明:本文件不構成英特爾關於其產品的使用和/或銷售的不論什麼明示或暗示的保證。包含不就其產品的(i)對某一特定用途的適用性、(ii)適銷性以及(iii)對不論什麼專利、版權或其它知識產權的侵害的承擔不論什麼責任或做出不論什麼擔保。

除非通過英特爾的書面容許承認,英特爾的產品無心被設計用於或被用於下面應用:即在這種應用中可因英特爾產品的故障而致使人身傷亡。

英特爾有權隨時更改產品的規格和描寫敘述,恕不另行通知。設計者不該信賴不論什麼英特產品所不具備的特性,設計者亦不該信賴不論什麼標有「保留權利」或「沒有定義」說明或特性描寫敘述。對此。英特爾保留未來對其進行定義的權利,同一時候。英特爾不該爲因其往後更改該等說明或特性描寫敘述而產生的衝突和不相容承擔不論什麼責任。此處提供的信息可隨時更改,恕不另行通知。請勿依據本文件提供的信息完畢一項產品設計。

本文件所描寫敘述的產品可能包括使其與宣稱的規格不符的設計缺陷或失誤。這些缺陷或失誤已收錄於勘誤表中。可索取得到。

在發出訂單以前。請聯繫當地的英特爾營業部或分銷商以獲取最新的產品規格。

如欲得到本文涉及的帶編號文檔的副本或其它英特爾文獻。可致電 1-800-548-4725。或訪問:http://www.intel.com/design/literature.htm

Optimization Notice in English

相關文章
相關標籤/搜索