android NDK開發及調用標準linux動態庫 so文件
預備知識及環境搭建
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開發的支持,
開發調用流程
一、新建一個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:變量必須包含將要編譯打包進模塊中的C或C++源代碼文件。註意,你不用在這裏列出頭文件和包含文件,因為編譯系統將會自動為你找出依賴型的文件;僅僅列出直接傳遞給編譯器的源代碼文件就好.
*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- #Test Exe
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_SRC_FILES:= main.c
- LOCAL_MODULE:= test_exe
- #LOCAL_C_INCLUDES := //加入所需要包含的頭文件路徑
- #LOCAL_STATIC_LIBRARIES := //加入所需要鏈接的靜態庫(*.a)的名稱,庫在系統的lib目錄下
- #LOCAL_SHARED_LIBRARIES := //加入所需要鏈接的動態庫(*.so)的名稱
- include $(BUILD_EXECUTABLE) //表示以一個可執行程序的方式進行編譯
編譯靜態庫的模板:
[cpp] view plaincopy- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_SRC_FILES:= main.c
- LOCAL_MODULE:= test_exe
- #LOCAL_C_INCLUDES := //加入所需要包含的頭文件路徑
- #LOCAL_STATIC_LIBRARIES := //加入所需要鏈接的靜態庫(*.a)的名稱,庫在系統的lib目錄下
- #LOCAL_SHARED_LIBRARIES := //加入所需要鏈接的動態庫(*.so)的名稱
- include $(BUILD_STATIC_LIBRARY) //表示以靜態庫的方式進行編譯
- <span style="color:#993300;">包含的頭文件路徑特別說明:</span>
- LOCAL_C_INCLUDES:=android/ftplib
- LOCAL_C_INCLUDES+=android
- LOCAL_C_INCLUDES+=android/adapi
- android、android/ftplib、android/ftplib:其中的android是在工程根目錄下的文件夾
編譯動態庫模版:
[cpp] view plaincopy- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE:= helloworld
- LOCAL_SRC_FILES := helloworld.c
- include $(BUILD_SHARED_LIBRARY)
編譯兩個動態庫模塊:
[cpp] view plaincopy- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- #清除一些變量
- LOCAL_MODULE := test
- #要生成的庫名
- LOCAL_SRC_FILES := testso.cpp
- #庫對應的源文件
- include $(BUILD_SHARED_LIBRARY)
- include $(CLEAR_VARS)
- #清除一些變量
- LOCAL_MODULE := WsJniPlayer
- #定義另外一個庫的名
- LOCAL_SRC_FILES := WsJniPlayer.cpp
- #定義庫對應的源文件
- LOCAL_LDLIBS := -ldl -llog
- #編譯你的模塊要使用的附加的鏈接器選項
- include $(BUILD_SHARED_LIBRARY)
編譯兩個動態庫,其中一個是第三方庫:
[cpp] view plaincopy- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE := libtest
- LOCAL_SRC_FILES := libtest.so
- include $(PREBUILT_SHARED_LIBRARY)
- include $(CLEAR_VARS)
- #清除一些變量
- LOCAL_SRC_FILES := WsJniTest.cpp
- #定義庫對應的源文件
- LOCAL_MODULE := WsJniTest
- #定義另外一個庫的名
- LOCAL_LDLIBS := -ldl -llog//運行時需要動態加載庫
- include $(BUILD_SHARED_LIBRARY)
隱式調用第三方庫
[java] view plaincopy- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_SRC_FILES := test.cpp
- LOCAL_PRELINK_MODULE := false
- LOCAL_MODULE := libtest
- LOCAL_MODULE_TAGS := optional
- include $(BUILD_SHARED_LIBRARY)
- include $(CLEAR_VARS)
- LOCAL_SRC_FILES := WsJniPlayer.cpp
- LOCAL_CFLAGS := -ltest
- LOCAL_LDFLAGS := -L$(LOCAL_PATH)//要隱式調用庫的位置
- LOCAL_SHARED_LIBRARIES :=libtest//要隱式調用庫的名稱
- LOCAL_MODULE := WsJniPlayer
- LOCAL_MODULE_TAGS := optional
- 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文件