1. 程式人生 > >android NDK開發及調用標準linux動態庫 so文件

android NDK開發及調用標準linux動態庫 so文件

als 一起 當前 api 之前 flags 負責 文件的 基礎

預備知識及環境搭建

1、NDK(native development Kit)原生開發工具包,用來快速開發C、C++動態庫,並能自動將so文件和java應用一起打包成apk.對應:jni層c++開發

2、Cygwin:是windows平臺上模擬Linux運行環境的工具,即window平臺上的linux環境工具,so文件需要在linux平臺上編譯運行。對應:arm linux平臺

3、CDT:eclipse下的C/C++開發工具,需要在eclipse下安裝這個插件。對應:標準C++開發。應該可以通過這個工具開發、編譯arm平臺的C++程序

4、Sequoyah:Sequoyah插件用於設置Android工程對Native開發的支持,

make文件生成工具,幫助我們自動生成mk文件,需要在eclipse下安裝這個插件。對應:make文件生成

開發調用流程

一、新建一個android項目,此處命名為WsJniPlayer.

二、NDK本地支持

1、添加NDK本地支持:在新建項目上,右鍵-》Android Tools->Add Native Support,彈出對話框

技術分享圖片

按默認設置即可,點擊Finish按鈕後,在項目目錄中,多了一個名為jni的目錄,這個目錄便是Sequoyah自動生成的用於存放C/C++源代碼的目錄

技術分享圖片

jni目錄中有兩個文件Android.mk和cpp文件WsJniPlayer.cpp

2、mk文件說明

菜鳥級別解釋::=是賦值的意思,$是引用某變量的值,include是執行動作。GNU:(GNU‘s Not Unix),GNU計劃,為保證GNU軟件可以自由地“使用、復制、修改和發布”,GNU通用公共許可證(GNU General Public License,GPL)。即“反版權”(或稱Copyleft)概念。

一個Android.mk file用來向編譯系統描述你的源代碼。具體來說:-該文件是GNU Makefile的一小部分。這個文件的語法允許把你的源代碼組織成模塊:靜態庫.a和共享庫.so。你可以在每一個Android.mk file中定義一個或多個模塊。下面是常用語句的解釋:

*LOCAL_PATH:= $(call my-dir):首先必須定義好LOCAL_PATH變量。它用於在開發樹中查找源文件。宏函數’my-dir,由編譯系統提供,用於返回當前路徑(即包含Android.mk file文件的目錄)。

*include $( CLEAR_VARS):CLEAR_VARS由編譯系統提供,指定讓GNU MAKEFILE為你清除許多LOCAL_XXX變量,除LOCAL_PATH

*LOCAL_MODULE := helloworld:LOCAL_MODULE變量必須定義,以標識你在Android.mk文件中描述的每個模塊。名稱必須是唯一的,而且不包含任何空格。註意編譯系統會自動產生合適的前綴和後綴,換句話說,一個被命名為‘foo‘的共享庫模塊,將會生成‘libfoo.so‘文件。重要註意事項:如果你把庫命名為‘libhelloworld’,編譯系統將不會添加任何的lib前綴,也會生成libhelloworld.so,這是為了支持來源於Android平臺的源代碼的Android.mk文件,如果你確實需要這麽做的話。

*LOCAL_SRC_FILES := helloworld.c:變量必須包含將要編譯打包進模塊中的CC++源代碼文件。註意,你不用在這裏列出頭文件和包含文件,因為編譯系統將會自動為你找出依賴型的文件;僅僅列出直接傳遞給編譯器的源代碼文件就好.

*include $(BUILD_SHARED_LIBRARY):BUILD_SHARED_LIBRARY是編譯系統提供的變量,指向一個GNU Makefile腳本,負責收集自從上次調用‘include $(CLEAR_VARS)‘以來,定義在LOCAL_XXX變量中的所有信息,並且決定編譯什麽,如何正確地去做。並根據其規則生成共享庫

make文件變量:GNU Make變量:在你的Android.mk文件解析之前,就由編譯系統定義好了;自己定義的變量:為了方便在Android.mk中定義自己的變量,我們建議使用MY_前綴

Android.mk使用模板

在一個Android.mk中可以生成多個可執行程序、動態庫和靜態庫。

編譯應用程序

[cpp] view plaincopy
  1. #Test Exe
  2. LOCAL_PATH := $(call my-dir)
  3. include $(CLEAR_VARS)
  4. LOCAL_SRC_FILES:= main.c
  5. LOCAL_MODULE:= test_exe
  6. #LOCAL_C_INCLUDES := //加入所需要包含的頭文件路徑
  7. #LOCAL_STATIC_LIBRARIES := //加入所需要鏈接的靜態庫(*.a)的名稱,庫在系統的lib目錄下
  8. #LOCAL_SHARED_LIBRARIES := //加入所需要鏈接的動態庫(*.so)的名稱
  9. include $(BUILD_EXECUTABLE) //表示以一個可執行程序的方式進行編譯

編譯靜態庫的模板:

[cpp] view plaincopy
  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)
  3. LOCAL_SRC_FILES:= main.c
  4. LOCAL_MODULE:= test_exe
  5. #LOCAL_C_INCLUDES := //加入所需要包含的頭文件路徑
  6. #LOCAL_STATIC_LIBRARIES := //加入所需要鏈接的靜態庫(*.a)的名稱,庫在系統的lib目錄下
  7. #LOCAL_SHARED_LIBRARIES := //加入所需要鏈接的動態庫(*.so)的名稱
  8. include $(BUILD_STATIC_LIBRARY) //表示以靜態庫的方式進行編譯
[cpp] view plaincopy
  1. <span style="color:#993300;">包含的頭文件路徑特別說明:</span>
[cpp] view plaincopy
  1. LOCAL_C_INCLUDES:=android/ftplib
  2. LOCAL_C_INCLUDES+=android
  3. LOCAL_C_INCLUDES+=android/adapi
[cpp] view plaincopy
  1. android、android/ftplib、android/ftplib:其中的android是在工程根目錄下的文件夾

編譯動態庫模版:

[cpp] view plaincopy
  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)
  3. LOCAL_MODULE:= helloworld
  4. LOCAL_SRC_FILES := helloworld.c
  5. include $(BUILD_SHARED_LIBRARY)

編譯兩個動態庫模塊:

[cpp] view plaincopy
  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)
  3. #清除一些變量
  4. LOCAL_MODULE := test
  5. #要生成的庫名
  6. LOCAL_SRC_FILES := testso.cpp
  7. #庫對應的源文件
  8. include $(BUILD_SHARED_LIBRARY)
  9. include $(CLEAR_VARS)
  10. #清除一些變量
  11. LOCAL_MODULE := WsJniPlayer
  12. #定義另外一個庫的名
  13. LOCAL_SRC_FILES := WsJniPlayer.cpp
  14. #定義庫對應的源文件
  15. LOCAL_LDLIBS := -ldl -llog
  16. #編譯你的模塊要使用的附加的鏈接器選項
  17. include $(BUILD_SHARED_LIBRARY)

編譯兩個動態庫,其中一個是第三方庫:

[cpp] view plaincopy
  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)
  3. LOCAL_MODULE := libtest
  4. LOCAL_SRC_FILES := libtest.so
  5. include $(PREBUILT_SHARED_LIBRARY)
  6. include $(CLEAR_VARS)
  7. #清除一些變量
  8. LOCAL_SRC_FILES := WsJniTest.cpp
  9. #定義庫對應的源文件
  10. LOCAL_MODULE := WsJniTest
  11. #定義另外一個庫的名
  12. LOCAL_LDLIBS := -ldl -llog//運行時需要動態加載庫
  13. include $(BUILD_SHARED_LIBRARY)

隱式調用第三方庫

[java] view plaincopy
  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)
  3. LOCAL_SRC_FILES := test.cpp
  4. LOCAL_PRELINK_MODULE := false
  5. LOCAL_MODULE := libtest
  6. LOCAL_MODULE_TAGS := optional
  7. include $(BUILD_SHARED_LIBRARY)
  8. include $(CLEAR_VARS)
  9. LOCAL_SRC_FILES := WsJniPlayer.cpp
  10. LOCAL_CFLAGS := -ltest
  11. LOCAL_LDFLAGS := -L$(LOCAL_PATH)//要隱式調用庫的位置
  12. LOCAL_SHARED_LIBRARIES :=libtest//要隱式調用庫的名稱
  13. LOCAL_MODULE := WsJniPlayer
  14. LOCAL_MODULE_TAGS := optional
  15. include $(BUILD_SHARED_LIBRARY)

LOCAL_SHARED_LIBRARIES和LOCAL_LDLIBS區別:

LOCAL_LDLIBS:鏈接的庫不產生依賴關系,一般用於不需要重新編譯的庫,如庫不存在,則會報錯找不到。且貌似只能鏈接那些存在於系統目錄下本模塊需要連接的庫。

LOCAL_SHARED_LIBRARIES :會生成依賴關系,當庫不存在時會去編譯這個庫。例如:開發中出現過這種情況,會編譯出一個錯誤的庫

3、cpp文件說明

自動生成的cpp文件是C++源代碼文件,這個默認的文件是空的,只包含兩include語句:#include <string>和#include <jni.h>

jni.h文件,定義了本地的數據類型,本地數據類型對應java類型前加上一個j,如int--->jint、jstring-->java.lang.String。本地數據類型是介於java類型和C++類型的中間類型,jni就是java和C++調用的橋。

特別需要註意的JNI調用的兩點規則:

a、java程序只能調用C語言接口,因此接口前要加上:extern "C"關鍵字。這是因為NDK主要是配合C語言開發,但是Sequoyah插件幫我們生成的是cpp文件。在默認情況下,會使用C++的編譯方式來進行編譯,這樣導致java調用時無法找到對應的接口函數。C和C++編譯時,可能函數名不一樣,也即:JAVA調用C編譯類型的函數名。

b、函數定義規則:在編寫函數時,函數名必須符合規則,不然JNI調用時無法找到需要的函數。

4、添加依賴包

代碼編寫後,可能發現報錯,提示找不到於與JNI相關的一些定義,這就需要添加NDK依賴包:項目右鍵-》properties->C/C++Gerneral->paths and symbols->includes->GNU C->add->file system,添加<ndk根目錄>\platforms\android-8\arch-arm\usr\include,添加完成後,重新build項目,即可解決。

註意:NDK的android-8版本同項目開發android版本對應關系:如android4.1<->android-14、android2.2<->android-8等,版本對應不上仍可能編譯不過。

三、編譯設置:

1、bash設置,即Cygwin設置:bash是一個為GNU計劃編寫的Unix shell,由於以bash命令運ndk-build等效於在Unix環境下運行ndk-build,所以要在windows環境變量path裏加入bash所在目錄,即cygwin\bin.

2、ndk-build設置:項目右鍵-》properties->C/C++Build,在Builder Settings選項卡中,取消Use default build command選項,並在Build Command 中填入:bash <ndk>\ndk-build,其中<ndk>為安裝的NDK的根目錄。

再分享一下我老師大神的人工智能教程吧。零基礎!通俗易懂!風趣幽默!還帶黃段子!希望你也加入到我們人工智能的隊伍中來!http://www.captainbed.net

android NDK開發及調用標準linux動態庫 so文件