1. 程式人生 > >NDK Android* 應用移植方法 APP_ABI := armeabi armeabi-v7a x86

NDK Android* 應用移植方法 APP_ABI := armeabi armeabi-v7a x86

概述

本指南用於幫助開發人員將現有的基於 ARM* 的 NDK 應用移植到 x86。如果您已經擁有一個正常執行的應用,需要知道如何能夠快速讓 x86 裝置在 Android* market 中找到您的應用,本文將可以為您提供一些入門資訊。同時本指南還提供了一些技巧和指南,以幫助您解決在移植過程中可能會遇到的編譯器問題。

目錄

NDK 概述

原生開發套件(NDK)是一款強大的工具,將原生 x86 程式碼的強大功能和 Android* 應用的圖形介面結合在一起。通過使用該工具,開發人員將能夠提升某些應用的效能優勢,但同時開發人員也需要十分謹慎,因為在某些情況並不能實現預期的效果。

NDK 致力於支援開發人員完成以下工作:

  • 編譯Android* 應用包中使用(由上層封裝程式 Java* 程式碼呼叫)的原生 C/C++ 庫。
  • 基於x86(英特爾® 凌動™ 處理器)重新編譯 ARM* 原生庫,並在必要時進行移植。

對於以上提到的第二點,部分情況下可能只需要簡單地修改 Build 標記並重新編譯即可,但有時卻並不這麼簡單。例如,如果原生庫涉及到 C 程式碼內的內嵌彙編,那麼程式碼將無法通過簡單的彙編來實現在兩種不同的架構自如執行的目標,此時將需要進行重新編寫(更多資訊請參見比較 ARM* 的 NEON* 與英特爾的 SSE 的部分)。

JNI 的效能影響和開銷

Java* 原生介面(JNI)將 Android* Java* 程式碼與由 NDK 預編譯的原生程式碼結合在一起。如欲瞭解有關該介面的更多資訊,請訪問:

http://java.sun.com/docs/books/jni/.

以上鍊接對 JNI 規範進行了廣泛、深入的剖析。若只是希望瞭解其概況,Wiki 頁面即可滿足您的要求(存在疑問時,請隨時參考規範以檢查其正確性)。Wiki 頁面的網址如下:http://en.wikipedia.org/wiki/Java_Native_Interface.

JNI 的開銷十分龐大,因此在理想情況下,開發人員應在應用中儘可能減少對 JNI 的呼叫。具體而言,在 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 檔案的內容

構件系統將在前端新增“lib”,生成名為 libtest.so 的庫。在 LOCAL_SRC_FILES 中,開發人員將提供工程原始檔的名稱。LOCAL_LDLIBS 和 LOCAL_CFLAGS 分別用於指定連結標記和編譯標記。

命令列構建

後面命令列提供了一個有關如何指定構建目標為 x86 架構的示例: ndk-build APP_ABI=x86

呼叫原生庫可採用以下兩種方法:System.loadLibrary(“relative_path_and_name”)和 System.load(“full_path_to_lib_file”)。前者更常用且更穩定。使用前者時,Android.mk 指定的庫名稱中的“lib”部分可丟棄。呼叫示例如下:


圖 1:呼叫原生程式碼示例

此外,對於原生程式碼,開發人員需要確保原生程式碼的輸入方法具有正確的 JNIEXPORT 方法簽名,而不是典型的 C/C++ 標頭。前面提及的 JNI 連結包含有更多相關資訊。

處理原生庫檔案

開發人員可通過兩種方式載入原生庫:1) 在 Android* apk 包中提供該庫並在執行時對其進行引用;2) 在 Android* 檔案系統上提供通往該庫的絕對路徑。採用以上方式中的哪一種取決於開發人員的偏好。但無論採用哪種方式,均應進行相應的正確處理。

執行時驗證

通過使用 adb logcat 命令,開發人員可確保在執行時成功載入目標原生庫。以下提供了一個描述原生庫已載入的系統日誌的示例。注意提供了通往原生庫檔案的完整路徑。


圖 2:呼叫原生程式碼示例

總結

以上各部分提供了有關如何使用 NDK 的入門知識。如欲瞭解更加複雜的細節,請閱讀 NDK 庫中包含的相關文件。這些文件提供有出色的教程和針對各種應用的原始碼示例。

移植概述

對於大多數應用而言,將現有的 NDK 應用移植到 x86 非常簡單。除非原生程式碼使用 ARM* 特有的特性,否則移植應用只需進行重新編譯、重新打包和重新發布操作即可完成。

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

  1. 獲取最新的 NDK 工具。x86 支援最先在 android-ndk-r6 中提供,但當時仍存在一些問題,之後谷歌很快進行了修復。確保您已經從 Android NDK 網站下載和安裝了最新的NDK(在本文撰寫之時,最新的 NDK 版本為 android-ndk-r6b)。 Android* NDK site.
  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 應當非常簡單,儘管很多人可能都有這樣的想法,但在實際的程式碼中,仍需要謹慎注意並解決英特爾® 凌動™ 和 ARM* 架構之間的差異。以下主題介紹了您在移植過程中可能遇到的問題以及如何予以解決。

工具鏈相容性

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

android-ndk\toolchains\arm-linux-androideabi-4.4.3
x86所用的路徑:
android-ndk\toolchains\x86-4.4.3
更多資訊請參考NDK文件android-ndk/docs/STANDALONE-TOOLCHAIN.html.

記憶體對齊影響:比較 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:英特爾推出的類似 NEON 的工具

SSE 指面向英特爾架構(IA)的SIMD 流指令擴充套件。目前,英特爾® 凌動™ 最高支援 SSSE3(補充 SIMD 流指令擴充套件 3)。凌動™ 暫不支援 SSE4.x。後者也是一個 128 位引擎,用於打包浮點資料。這一執行模式開始於 MMX 技術。SSx 是較新的技術,取代了 MMX。。如欲瞭解詳細資訊,請參閱英特爾《IA-32 和 IA-64 軟體開發人員手冊》中的“第一卷:基礎架構”部分。網址為:http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html。目前,SSE 概述部分在 5.5 節。這一章節提供了 SSE、SSE2、SSE3 和 SSSE3 的 Opcode(操作碼)。注意,資料運算通常會涉及到處理基於精度的打包浮點數值;並且需要在 XMM 暫存器之間,或在這些暫存器與記憶體之間批量傳輸資料。XMM 暫存器主要用於取代 MMX 暫存器。

NEON 與 SSE 在彙編層面的比較

在該連結中,您可通過“目錄”部分直接跳到程式碼示例或首先詳細瞭解某些背景資訊。同樣,以下直接來自 ARM* 的手冊提供了一些資訊和 NEON* 彙編小片段: /sites/default/files/m/b/4/c/DHT0002A_introducing_neon.pdf。請參閱 ARM* 文件中的第 1.4 節。

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

結論

我們希望這一指南能夠為您提供一些實用資訊,幫助您成功將基於 NDK 的應用移植到 x86。移植到 x86 後,您的應用將可以供一種全新型別的 Android* 裝置進行下載、購買和使用。如果您在移植過程中遇到問題,請隨時在本文中發表評論。我們將非常樂意回答您的問題,為您提供幫助。

宣告

*文中涉及的其它名稱及商標屬於各自所有者資產。

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

英特爾、Intel、凌動和 Atom 是英特爾公司在美國和其他國家(地區)的商標。

本文所提供之資訊均與英特爾產品相關。本文不代表英特爾公司或其它機構向任何人明確或隱含地授予任何智慧財產權。除相關產品的英特爾銷售條款與條件中列明之擔保條件以外,英特爾公司不對銷售和/或使用英特爾產品做出任何其它明確或隱含的擔保,包括對適用於特定用途、適銷性,或不侵犯任何專利、版權或其它智慧財產權的擔保。

除非經英特爾書面同意,英特爾產品並非設計或有意用於任何英特爾產品故障可能會引起人身傷亡事故的應用領域。

英特爾可以隨時在不釋出宣告的情況下修改規格和產品說明。設計者不得依賴於帶有“保留”或“未定義”的任何特性或說明。英特爾保留今後對其定義的權利,對於因今後對其進行修改所產生的衝突或不相容性概不負責。此處資訊可能隨時更改,恕不另行通知。請勿使用本資訊來對某個設計做出最終決定。

文中所述產品可能包含設計缺陷或錯誤,已在勘誤表中註明,這可能會使產品偏離已經發布的技術規範。英特爾提供最新的勘誤表備索。

訂購產品前,請聯絡您當地的英特爾銷售辦事處或分銷商瞭解最新技術規範。

Optimization Notice in Chinese