TShopping

 找回密碼
 註冊
搜索
查看: 365|回復: 1

[教學] Android NDK介紹(下)

[複製鏈接]
發表於 2014-10-22 15:03:49 | 顯示全部樓層 |閱讀模式
 
Push to Facebook Push to Plurk Push to Twitter 
為了在Android OS系統上開發應用程序,Google提供了兩種開發包:SDK和NDK。你可以從Google官方查閱到有許多關於SDK的優秀的書籍、文章作為參考,但Google沒有提供足夠的NDK資料。在現有的書籍中,我認為Cinar O.寫於2012年的”Pro Android C++ with the NDK”值得一讀。

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

ImportNew注:如果你也對Java技術翻譯分享感興趣,歡迎加入我們的Android開發小組。參與方式請查看小組簡介。

JNI函數:

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

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

JNI函數使用示例

下面會通過一個簡短的示例確保你對這些資料所講的內容有了正確的理解:
  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 — 提供了一個接口,可以調用函數創建、刪除Java虛擬機。
JNIEnv — 確保了大多數的JNI函數。
JavaVMlnitArgs — Java虛擬機參數。
JavaVMOption — Java虛擬機選項。
JNI的_CreateJavaVM()方法初始化Java虛擬機並向JNI接口返回一個指針。

JNI_DestroyJavaVM()方法可以載入創建好的Java虛擬機。

線程

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

第一步

你的項目結構應該如圖所示: 1.jpeg

圖—工程結構
在圖中,所有本地代碼都存儲到一個jni的文件夾。在新建一個工程後,Libs文件夾會被分為四個子文件夾。這意味著一個子目錄對應一種處理器架構,庫的數量取決於處理器架構的數量。

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

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


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

最精簡的配置示例:
  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)
複製代碼
讓我們來仔細看看:

LOCAL_PATH:-$(call my-dir) — 調用函數宏my-dir返回當前文件所在路徑。
include $(CLEAR_VARS) — 清除所有LOCAL_PATH以外的變量。這是必須的步驟,考慮到所有編譯控製文件都位於同一個GNU MAKE執行環境中,所有變量都是全局的。
LOCAL_MODULE — 輸出模塊名稱。在上述例子中,輸出模塊叫做NDKBegining。但是在生成以後,會在libs文件夾中創建libNDKbegining庫。同時,Android系統會為其添加一個前綴名lib,例如一個被命名為”foo”的共享庫模塊,將會生成”libfoo.so”文件。但是在Java代碼中使用庫時應該忽略前綴名(也就是說,名稱應該和makefile一樣)。
LOCAL_SRC_FILE — 列出編譯所需要的源文件。
include $(BUILD_SHARED_LIBARY) — 輸出模塊的類型。
你可以在Android.mk文件中設置自定義變量;但是必須遵守語法命名規則:LOCAL_、PRIVATE_、NDK_、APP_、my-dir。Google建議自定義示例前綴使用MY_,例如:
  1. MY_SOURCE := NDKBegining.c
複製代碼
這樣就調用了一個變量$(MY_SOURCE)。變量同樣也可以被連接起來,例如:
  1. LOCAL_SRC_FILES += $(MY_SOURCE)
複製代碼
Application.mk

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

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

NDK-build是一個GNU Make的包裝容器。在NDK 4以後,ndk-build支持以下參數:

clean — 清除所有已生成的二進製文件。
NDK_DEBUG=1 — 生成可調式的代碼。
NDK_LOG=1 — 顯示日誌信息(用於調試)。
NDK_HOST_32BIT=1 — 使Android系統支持64位版本(例如,NDK_PATH\toolchains\mipsel-linux-android-4.8\prebuilt\windows-x86_64,等等)。
NDK_APPLICATION_MK=<file> — 指定Application.mk路徑。
在NDK v5中,引入了NDK_DEBUG。當NDK_DEBUG設置為“1”時,便會生成可調試版本。如果沒有設置NDK_DEBUG,ndk-build會默認驗證是否有在AndroidMainfest.xml文件中設置android:debuggable=“true” 屬性。如果你使用的是NDK v8以後的版本,Google不建議你在AndoirdMainfest.xml文件中使用android:debuggable 屬性(當你使用“ant debug”或ADT插件生成調試版本時,會自動添加“NDK_DEBUG=1” )。

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

如何建立一個項目?

這是個令人頭疼的步驟。你要安裝CDT插件並下載cygwin或mingw編譯器和Android NDK,在Eclipse設置裡配置這些東西,但最後還是不能運行。我第一次開始使用Android NDK時,配置這些東西花了我3天時間。最後發現問題出在Cygwin編譯器身上:應該為項目文件夾設置讀、寫、可執行的所有權限。

現在可就簡單多咯!只需要照著這個鏈接到網址:http://developer.android.com/sdk/index.html  下載ADT包,這裡面有開始編譯環節需要用到的所有東西。

從Java代碼中調用本地方法

要從Java中調用本地代碼,首先你要在Java類中定義本地方法。例如:
  1. native String nativeGetStringFromFile(String path) throws IOException;
  2. native void nativeWriteByteArrayToFile(String path, byte[] b) throws IOException
複製代碼
你得在方法前使用“native”關鍵字。,這樣編譯器就知道這是JNI的入口點。這些方法會在C/C++文件中實現。Google建議用“native+x”這樣的命名方式,“x”代表著方法的實際名稱。還有,在實現這些方法前你還得手動生成一個頭文件。你可以手動執行此操作或者使用JDK的javah工俱生成頭文件。然後讓我們將進一步探討如何不用控制台,直接使用標準的Eclipse開發環境:

打開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}»)。
填入如下參數:“-jni ${java_type_name}” (注意,輸入時不需要帶引號)。
現在你可以運行了。你的頭文件應該放在bin/classes目錄下。下一步,複製這些文件到本地工程的jni目錄。打開工程的配置菜單並選擇Andorid Tools這一項— 添加本地庫(Add Native Library)。這樣我們就可以使用jni.h頭文件中包含的函數了。在此之後,你還要創建一個.cpp的文件(有時候Eclipse會默認生成),並且方法實現已經在頭文件中定義。

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

 

臉書網友討論
發表於 2014-10-23 14:47:44 | 顯示全部樓層


  真有如此?

版主招募中

您需要登錄後才可以回帖 登錄 | 註冊 |

本版積分規則



Archiver|手機版|小黑屋|免責聲明|TShopping

GMT+8, 2016-12-10 18:59 , Processed in 0.061080 second(s), 24 queries .

本論壇言論純屬發表者個人意見,與 TShopping綜合論壇 立場無關 如有意見侵犯了您的權益 請寫信聯絡我們。

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回復 返回頂部 返回列表