Android.mk走讀與Cmake配置

Android.mk認識:html

在上一次【http://www.javashuo.com/article/p-xnceylrr-s.html】中學會了用NDK提供的交叉編譯工程編譯成Android能運行的可執行文件,下面我們來作個實驗來看一下使用靜態庫與動態庫的區別,仍是用上一次用的源文件爲例:java

動態庫的具體的生成過程能夠參考上一次寫的博文,接下來再生成一個靜態庫,如何生成呢?linux

因此我們先來找到NDK提供的ar交叉編譯工具:android

因此我們使用它依照生成規則來生成對應的靜態庫看一下:c++

接下來我們新建一個Android工程,來使用編譯出來的動靜態庫,這裏仍是創建一個純的非NDK的Android工程,來進一步操練下如何給一個普通Android工程來集成NDK,以下:web

而後我們來集成ndk環境,回顧上一次的步驟,得修改app下的build.gradle配置了:shell

這個是針對源文件的,而咱們想要用Android.mk來進行構建就須要在外層來指定了,以下:windows

 

其實上一次已經使用過了,裏面Android.mk的編寫規則也記不住,直接拷貝一下,重點不是記着,仍是你能讀懂就成,以下:api

而後我們在對應位置創建一個native-lib.c的源文件:ruby

那我們編譯一個apk,而後能夠看到apk中已經有一個.so文件了,以下:

那若是想使用咱們剛編譯寫的libTest.so文件應該怎麼使用呢?這裏就涉及到多模塊的引入問題,先將咱們編譯好的libTest.so拷入到工程中:

而後在Android.mk中來聲明一個新的模塊,其寫法跟聲明.c源文件相似,以下:

而後我們再來編譯一個apk,看一下里面的.so的狀況,是否是變爲2個.so了?

那是由於我們新聲明的預編譯模塊尚未使用到,因此須要關聯一下,以下:

那此時我們來在本身的.c文件中來使用一下預編譯庫定義的函數,以下:

固然在運行以前還得加載一個咱們編譯的庫:

而後編譯生成一個apk,看此時它裏面包含動態庫的狀況:

記住動態庫的這個現象哈~~以後會用靜態庫作一個對比!!!!

而後下面分別在Android4.3系統和Android8.1.0的系統上運行,爲啥要在兩個版本上運行,由於確定是有坑嘛~~,因此下面先來看在Android4.3上運行,用的手機型號爲:

崩潰了,看一下崩潰日誌:

11-27 09:12:04.756 6028-6028/com.jni.test E/AndroidRuntime: FATAL EXCEPTION: main
    java.lang.UnsatisfiedLinkError: dlopen failed: could not load library "/Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/build/intermediates/ndkBuild/debug/obj/local/armeabi-v7a/libTest.so" needed by "libnative-lib.so"; caused by library "/Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/build/intermediates/ndkBuild/debug/obj/local/armeabi-v7a/libTest.so" not found
        at java.lang.Runtime.loadLibrary(Runtime.java:362)
        at java.lang.System.loadLibrary(System.java:525)
        at com.jni.test.MainActivity.<clinit>(MainActivity.java:9)
        at java.lang.Class.newInstanceImpl(Native Method)
        at java.lang.Class.newInstance(Class.java:1130)
        at android.app.Instrumentation.newActivity(Instrumentation.java:1078)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2223)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2362)
        at android.app.ActivityThread.access$700(ActivityThread.java:168)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1329)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:177)
        at android.app.ActivityThread.main(ActivityThread.java:5493)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:525)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1225)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1041)
        at dalvik.system.NativeStart.main(Native Method)

找不到libTest.so,這是由於在Android6.0如下版本,System.loadLibrary()不會自動爲咱們加載依賴的動態庫,若是load一個動態庫,則須要先將這個動態庫的依賴的其餘動態庫也要load進來,因此:

那此時一樣的代碼在Android8.1.0的手機上來運行,手機型號是:

又崩潰了,崩潰日誌:

    --------- beginning of crash
11-27 09:26:34.879 15598-15598/? E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.jni.test, PID: 15598
    java.lang.UnsatisfiedLinkError: dlopen failed: library "/Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/build/intermediates/ndkBuild/debug/obj/local/armeabi-v7a/libTest.so" not found
        at java.lang.Runtime.loadLibrary0(Runtime.java:1016)
        at java.lang.System.loadLibrary(System.java:1660)
        at com.jni.test.MainActivity.<clinit>(MainActivity.java:10)
        at java.lang.Class.newInstance(Native Method)
        at android.app.Instrumentation.newActivity(Instrumentation.java:1179)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3054)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3278)
        at android.app.ActivityThread.-wrap12(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1894)
        at android.os.Handler.dispatchMessage(Handler.java:109)
        at android.os.Looper.loop(Looper.java:166)
        at android.app.ActivityThread.main(ActivityThread.java:7377)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:469)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:963)

標紅處的地址很顯示是個人mac電腦上的路徑了,在手機上確定找不到嘛,那爲啥在8.1.0的手機上運行會出現從這個路徑去加載預編譯的動態庫呢?其實這就是版本兼容的問題了,在NDK中提供了一個能夠看到.so文件的依賴狀況,以下:

那我們來查看一下咱們的libnative-lib.so它裏面的依賴狀況,先定位到這個生成的.so文件的路徑處:

而後使用這個ndk的依賴查看命令:

也正好是崩潰日誌中所看到的路徑,實際上是從Android6.0開始,使用Android.mk若是來引入一個預編譯動態庫是有問題的【這個須要特別的注意!】,其緣由也就是「Android6.0如下版本,System.loadLibrary()不會自動爲咱們加載依賴的動態庫,而6.0及以上的版本是會自動爲咱們加載依賴的動態庫」,也就是說在Android6.0以上版本不用咱們顯示的去寫加載的預編譯庫了,也就是這句話在Android6.0上能夠省略掉了:

那使用預編譯的動態庫在Android6.0以上系統有問題,那若是是改用靜態庫會不會有問題呢?我們試試,將以前咱們編譯好的靜態庫也放到工程當中:

此時須要修改.mk文件了,以下:

其中關於MK中的這些變量的使用能夠參考:

經常使用內置變量

變量名 含義 示例
BUILD_STATIC_LIBRARY 構建靜態庫的Makefile腳本 include $(BUILD_STATIC_LIBRARY)
PREBUILT_SHARED_LIBRARY 預編譯共享庫的Makeifle腳本 include $(PREBUILT_SHARED_LIBRARY)
PREBUILT_STATIC_LIBRARY 預編譯靜態庫的Makeifle腳本 include $(PREBUILT_STATIC_LIBRARY)
TARGET_PLATFORM Android API 級別號 TARGET_PLATFORM := android-22
TARGET_ARCH CUP架構 arm arm64 x86 x86_64
TARGET_ARCH_ABI CPU架構 armeabi armeabi-v7a arm64-v8a

模塊描述變量

變量名 描述
LOCAL_MODULE_FILENAME 覆蓋構建系統默認用於其生成的文件的名稱 LOCAL_MODULE := foo LOCAL_MODULE_FILENAME := libnewfoo
LOCAL_CPP_FEATURES 特定 C++ 功能 支持異常:LOCAL_CPP_FEATURES := exceptions
LOCAL_C_INCLUDES 頭文件目錄查找路徑 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_CFLAGS 構建 C  C++ 的編譯參數  
LOCAL_CPPFLAGS c++  
LOCAL_STATIC_LIBRARIES 當前模塊依賴的靜態庫模塊列表  
LOCAL_SHARED_LIBRARIES    
LOCAL_WHOLE_STATIC_LIBRARIES --whole-archive 將未使用的函數符號也加入編譯進入這個模塊
LOCAL_LDLIBS 依賴 系統庫 LOCAL_LDLIBS := -lz

導出給引入模塊的模塊使用:

LOCAL_EXPORT_CFLAGS

LOCAL_EXPORT_CPPFLAGS

LOCAL_EXPORT_C_INCLUDES

LOCAL_EXPORT_LDLIBS

編譯下編譯生成一個apk:

也就是說會將咱們程序用到的libTest.a靜態庫中的那段代碼抽離打包到咱們本身的so當中,而動態庫是整個會打包進apk,跟咱們本身的so是獨立的,而後在運行時進行依賴調用,也就是說其實使用靜態作爲預編譯庫來使用的話打出來的.so會小不少,由於只有咱們使用到的纔會打包,而動態庫是整個都會打包,無論使用了仍是木有使用。舉個形象的例子來理解靜態庫與動態庫打包的區別:

假設有個jar裏面包含a.java、b.java、c.java,而後我們app有一個app.java,它使用了jar包中的a.java,若是使用了靜態庫打包,則最終apk中只包含app.java和a.java;而若是使用了動態庫打包,則最終APK中包含app.java和整個jar(a.java、b.java、c.java),很顯示在開發中通常使用靜態庫較好,可是有個問題:爲啥三方SDK通常都是提供.so文件呢?其實根本緣由仍是以前講的:它只能加載動態庫,不加載靜態庫:

只是說若是是咱們本身編譯的庫可使用靜態庫,由於咱們有NDK的編譯環境嘛,若是三方給一個.a靜態庫,那咱們在工程中仍是配置NDK編譯環境來將它進行一個封裝,最終編成咱們的.so再來使用,太麻煩了。 

此時再運行,在不一樣版本上均可以兼容了。總之須要記住在mk中引入預編譯的動態庫是有版本性的差別滴,要解決的話就得用Cmake來代替.mk了,像現在的項目基本都是採用Cmake來進行NDK的編譯了,那學習.mk有啥意義呢?由於好多老工程的編譯仍是基於.mk的,咱們須要能看懂它,因此基於這個學習目的,下面來看一個cocos2d的一個hello world級別的工程,它裏面的工程編譯就是經過mk的形式,咱們來試着讀一下它的mk,看可否讀懂,能讀懂的話那目的就達到了:

首先固然先導入這個cocos2d的工程啦,具體工程的代碼能夠上網上搜搜,這裏導它的目的只是爲了去了解它的mk文件的編寫規則,並不是研究怎麼用它來開發遊戲,以下:

而後工程運行的界面以下:

我們重點關心的就是它的mk文件的編寫內容啦,所在的位置以下:

首先來看一下「Android.mk」完整配置:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)


#模塊名  若是沒有 LOCAL_MODULE_FILENAME 配置 就會生成一個
#libMyGame_shared.so
LOCAL_MODULE := MyGame_shared

#生成一個 libMyGame.so (能夠不寫)
LOCAL_MODULE_FILENAME := libMyGame

#源文件
LOCAL_SRC_FILES := $(LOCAL_PATH)/hellocpp/main.cpp \
                   $(LOCAL_PATH)/../../../Classes/AppDelegate.cpp \
                   $(LOCAL_PATH)/../../../Classes/HelloWorldScene.cpp

# 編譯時查找頭文件的路徑 至關於:-I
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../Classes

# _COCOS_HEADER_ANDROID_BEGIN
# _COCOS_HEADER_ANDROID_END

# 引入一個靜態庫做爲當前編譯源文件的依賴(jar)
# 定義在其餘的mk文件裏面
LOCAL_STATIC_LIBRARIES := cocos2dx_static

# _COCOS_LIB_ANDROID_BEGIN
# _COCOS_LIB_ANDROID_END

include $(BUILD_SHARED_LIBRARY)

# 單獨聲明是沒有任何意義的 須要結合 import-module 來看

# 設置引入其餘mk的查找路徑
$(call import-add-path,$(LOCAL_PATH)/../../../cocos2d)
$(call import-add-path,$(LOCAL_PATH)/../../../cocos2d/external)
$(call import-add-path,$(LOCAL_PATH)/../../../cocos2d/cocos)
$(call import-add-path,$(LOCAL_PATH)/../../../cocos2d/cocos/audio/include)
#引入其餘路徑下的Android.mk文件
# 至關於 #include
$(call import-module, cocos)



# _COCOS_LIB_IMPORT_ANDROID_BEGIN
# _COCOS_LIB_IMPORT_ANDROID_END

接下來就是嘗試來讀懂這裏面的規則,第一句不用多說,每一個Android.mk文件的開頭的固定寫法:

能夠瞅一下我們生成的apk中的.so是不是咱們指定的這個:

嗯,確實是,繼續往下看:

對就是對應於這些源文件:

關於-I參數的含義能夠回顧一下:

這個其實我們在以前的DEMO中已經用到過了,以下:

因此咱們查找一下"cocos2dx_static"這個模塊的定義,發現木有在當前的Android.mk中定義,而是在其它mk中,這就涉及到如何引入其它mk的module啦,具體使用以下:

比較抽像對吧,來回到cocos2d這塊的配置就能理解滴:

對應於工程的這塊:

而後我們打開它,看一下"cocos2dx_static"這個模塊是否認義在這個.mk裏面:

至此對於第一個Android.mk就分析完了,貌似也都能看懂嘛,接下來還有另一種mk,也就是下面要學習滴。

Application.mk:

一樣是GNU Makefile 片斷,在Application.mk中定義一些全局(整個項目)的配置

先來瞅一下工程中的這個mk:

先來看一下它裏面都配了啥:

# 指定運行時庫 (libc )
APP_STL := c++_static
#會交給編譯器的參數
APP_CPPFLAGS := -frtti -DCC_ENABLE_CHIPMUNK_INTEGRATION=1 -std=c++11 -fsigned-char -Wno-extern-c-compat
# 交給連接器的參數 (so依賴另外一個so,這就須要連接 )
APP_LDFLAGS := -latomic
# 要生成的cpu架構
APP_ABI := armeabi-v7a
#解決windows命令行不支持太長的字符輸入的問題
APP_SHORT_COMMANDS := true


                      ifeq ($(NDK_DEBUG),1)
                        APP_CPPFLAGS += -DCOCOS2D_DEBUG=1
                        APP_OPTIM := debug
                      else
                        APP_CPPFLAGS += -DNDEBUG
                        APP_OPTIM := release
endif

接下來一個個瞭解一下:

也就是咱們在寫cpp裏的一些系統函數,如std::cout之類的,具體這塊的配置參數以下:

而它的定義以下:

因爲這個工程的源代碼是基於cpp的,因此用上面這個標誌,若是是c,則須要用下面這個:

【注意】:因爲NDK對於mk已經接近放棄的階段,因此對於CPU的指定建議仍是要build.gradle中去指定,以下:

而它的具體配置以下:

須要生成的cpu架構(ndk r17 只支持:armeabi-v7a, arm64-v8a, x86, x86_64)

指令集
基於 ARMv7 的設備上的硬件 FPU 指令 APP_ABI := armeabi-v7a
ARMv8 AArch64 APP_ABI := arm64-v8a
IA-32 APP_ABI := x86
Intel64 APP_ABI := x86_64
MIPS32 APP_ABI := mips
MIPS64 (r6) APP_ABI := mips64
全部支持的指令集 APP_ABI := all

不一樣 Android 手機使用不一樣的 CPU,所以支持不一樣的指令集。

armeabi-v7a

armeabi-v7a ABI 使用 -mfloat-abi=softfp 開關強制實施規則,要求編譯器在函數調用時必須傳遞核心寄存器對中的全部雙精度值,而不是專用浮點值。 系統可使用 FP 寄存器執行全部內部計算。 這樣可極大地加速計算。

若是要以 armeabi-v7a ABI 爲目標,則必須設置下列標誌:

CFLAGS= -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 

arm64-v8a

此 ABI 適用於基於 ARMv八、支持 AArch64 的 CPU。它還包含 NEON 和 VFPv4 指令集。

x86

此 ABI 適用於支持一般稱爲「x86」或「IA-32」的指令集的 CPU。設置的標誌如:

-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32 

x86_64

-march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel 

如今手機主要是armeabi-v7a。查看手機cpu:

adb shell cat /proc/cpuinfo

adb shell getprop ro.product.cpu.abi

如查看一下我模擬器的cpu狀況:

xiongweideMacBook-Pro:LableCoffee xiongwei$ adb shell cat /proc/cpuinfo
processor    : 0
vendor_id    : GenuineIntel
cpu family    : 6
model        : 69
model name    : Intel(R) Core(TM) i5-4278U CPU @ 2.60GHz
stepping    : 1
cpu MHz        : 2600.058
cache size    : 3072 KB
physical id    : 0
siblings    : 4
core id        : 0
cpu cores    : 4
apicid        : 0
initial apicid    : 0
fpu        : yes
fpu_exception    : yes
cpuid level    : 13
wp        : yes
flags        : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx rdrand lahf_lm abm
bugs        :
bogomips    : 5200.11
clflush size    : 64
cache_alignment    : 64
address sizes    : 39 bits physical, 48 bits virtual
power management:

processor    : 1
vendor_id    : GenuineIntel
cpu family    : 6
model        : 69
model name    : Intel(R) Core(TM) i5-4278U CPU @ 2.60GHz
stepping    : 1
cpu MHz        : 2600.058
cache size    : 3072 KB
physical id    : 0
siblings    : 4
core id        : 1
cpu cores    : 4
apicid        : 1
initial apicid    : 1
fpu        : yes
fpu_exception    : yes
cpuid level    : 13
wp        : yes
flags        : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx rdrand lahf_lm abm
bugs        :
bogomips    : 5200.11
clflush size    : 64
cache_alignment    : 64
address sizes    : 39 bits physical, 48 bits virtual
power management:

processor    : 2
vendor_id    : GenuineIntel
cpu family    : 6
model        : 69
model name    : Intel(R) Core(TM) i5-4278U CPU @ 2.60GHz
stepping    : 1
cpu MHz        : 2600.058
cache size    : 3072 KB
physical id    : 0
siblings    : 4
core id        : 2
cpu cores    : 4
apicid        : 2
initial apicid    : 2
fpu        : yes
fpu_exception    : yes
cpuid level    : 13
wp        : yes
flags        : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx rdrand lahf_lm abm
bugs        :
bogomips    : 5200.11
clflush size    : 64
cache_alignment    : 64
address sizes    : 39 bits physical, 48 bits virtual
power management:

processor    : 3
vendor_id    : GenuineIntel
cpu family    : 6
model        : 69
model name    : Intel(R) Core(TM) i5-4278U CPU @ 2.60GHz
stepping    : 1
cpu MHz        : 2600.058
cache size    : 3072 KB
physical id    : 0
siblings    : 4
core id        : 3
cpu cores    : 4
apicid        : 3
initial apicid    : 3
fpu        : yes
fpu_exception    : yes
cpuid level    : 13
wp        : yes
flags        : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx rdrand lahf_lm abm
bugs        :
bogomips    : 5200.11
clflush size    : 64
cache_alignment    : 64
address sizes    : 39 bits physical, 48 bits virtual
power management:

xiongweideMacBook-Pro:LableCoffee xiongwei$ adb shell getprop ro.product.cpu.abi
x86

apk在安裝的時候,若是手機是armeabi-v7a的,則會首先查看apk中是否存在armeabi-v7a目錄,若是沒有就會查找armeabi。

保證cpu目錄下so數量一致。

​ 若是目標是armeabi-v7a,可是擁有一個armeabi的,也能夠把它放到armeabi-v7a目錄下。可是反過來不行

 

ABI(橫 so)/CPU(豎 手機) armeabi armeabi-v7a arm64-v8a x86 x86_64
ARMV5 支持        
ARMV7 支持 支持      
ARMV8 支持 支持 支持    
X86       支持  
X86_64       支持 支持

繼續仍是回到工程中的.mk往下瞅:

其中APP_OPTIM的具體含義以下:

以上就是關於mk的東東,配置比較複雜,不必去記,由於真實項目使用可能都會轉到下面要學習的cmake了,重點是要能讀取mk,而後能夠很容易的將mk轉成cmake就能夠啦~~

Cmake配置:

在android studio 2.2及以上,構建原生庫的默認工具是 CMake。

​ CMake是一個跨平臺的構建工具,能夠用簡單的語句來描述全部平臺的安裝(編譯過程)。可以輸出各類各樣的makefile或者project文件。Cmake 並不直接建構出最終的軟件,而是產生其餘工具的腳本(如Makefile ),而後再依這個工具的構建方式使用

​ CMake是一個比make更高級的編譯配置工具,它能夠根據不一樣平臺、不一樣的編譯器,生成相應的Makefile或者vcproj項目。從而達到跨平臺的目的。Android Studio利用CMake生成的是ninja,ninja是一個小型的關注速度的構建系統。咱們不須要關心ninja的腳本,知道怎麼配置cmake就能夠了。從而能夠看出cmake實際上是一個跨平臺的支持產出各類不一樣的構建腳本的一個工具。

CMake的腳本名默認是CMakeLists.txt

其中標紅處提到了一個新名詞「ninja」,其實它存在於咱們的SDK當中,以下:

固然咱們不用關心ninja是如何構建的啦,只需瞭解CMake如何作就成了,這裏作一個瞭解。

下面我們來基於以前學習mk的工程來改用cmake,工程回憶一下:

首先得修改gradle的ndk編譯腳本的路徑,以下:

而後在cmake裏面也用以前的native-lib.c這個源文件:

那接下來就是來在CMakeLists.txt中來編寫構造腳本啦,那,如何寫呢。。下面來學習下,首先得指定一下最低版本,以下:

注意一個小細節:

接下來指令要編譯的源文件及編譯出來是要靜態庫或動態庫,以下:

此時編譯一下:

再次編譯:

這裏經過創建一個帶NDK環境的工程的這塊的配置一對比,發現它的ndk配置是這樣寫的:

那我們依葫蘆畫瓢一下:

再編譯一下:

Build command failed.
Error while executing process /Users/xiongwei/android-sdks/Android/sdk/cmake/3.6.3155560/bin/cmake with arguments {--build /Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/.externalNativeBuild/cmake/debug/mips64 --target native-lib}
[1/1] Linking C shared library /Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/build/intermediates/cmake/debug/obj/mips64/libnative-lib.so
FAILED: : && /Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang  --target=mips64el-none-linux-android --gcc-toolchain=/Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/toolchains/mips64el-linux-android-4.9/prebuilt/darwin-x86_64 --sysroot=/Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/platforms/android-21/arch-mips64 -fPIC -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -Wa,--noexecstack -Wformat -Werror=format-security  -O0 -fno-limit-debug-info  -Wl,--build-id -Wl,--warn-shared-textrel -Wl,--fatal-warnings -Wl,--no-undefined -Wl,-z,noexecstack -Qunused-arguments -Wl,-z,relro -Wl,-z,now -shared -Wl,-soname,libnative-lib.so -o /Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/build/intermediates/cmake/debug/obj/mips64/libnative-lib.so CMakeFiles/native-lib.dir/native-lib.c.o  -lm && :
CMakeFiles/native-lib.dir/native-lib.c.o: In function `Java_com_jni_test_MainActivity_nativeTest':
/Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/src/main/cmake/native-lib.c:7: undefined reference to `test'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.

很顯然在我們的源代碼中引用了動態庫或靜態庫中的函數,可是咱們在CMakeLists.txt中並未指定動態庫或靜態庫的配置,因此接下來我們先以引入靜態庫爲例:

那從哪裏來導入呢,接下來須要設置一下靜態庫的路徑:

因爲libTest.a跟咱們的CMakeLists.txt不在同一個目錄,因此路徑須要注意一下,最後一步須要進行連接,也就是將這兩個模塊須要關聯起來:

如何寫呢?以下:

好,一切設置完畢,我們試着來編譯一下看是否正常了:

Build command failed.
Error while executing process /Users/xiongwei/android-sdks/Android/sdk/cmake/3.6.3155560/bin/cmake with arguments {--build /Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/.externalNativeBuild/cmake/debug/mips64 --target native-lib}
ninja: error: '../../cpp/libTest.a', needed by '/Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/build/intermediates/cmake/debug/obj/mips64/libnative-lib.so', missing and no known rule to make it

貌似是靜態庫的路徑這樣設置不行,怎麼辦呢?其實能夠嘗試將CMakeLists.txt文件挪一下位置,我們先來看一下目錄結構:

挪了位置以後,固然build.gradle的腳本配置那塊也得變下了,以下:

而後在CMakeLists.txt路徑那能夠修改成:

而後再編譯一下:

Build command failed.
Error while executing process /Users/xiongwei/android-sdks/Android/sdk/cmake/3.6.3155560/bin/cmake with arguments {--build /Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/.externalNativeBuild/cmake/debug/mips64 --target native-lib}
[1/1] Linking C shared library ../../../../build/intermediates/cmake/debug/obj/mips64/libnative-lib.so
FAILED: : && /Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang  --target=mips64el-none-linux-android --gcc-toolchain=/Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/toolchains/mips64el-linux-android-4.9/prebuilt/darwin-x86_64 --sysroot=/Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/platforms/android-21/arch-mips64 -fPIC -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -Wa,--noexecstack -Wformat -Werror=format-security  -O0 -fno-limit-debug-info  -Wl,--build-id -Wl,--warn-shared-textrel -Wl,--fatal-warnings -Wl,--no-undefined -Wl,-z,noexecstack -Qunused-arguments -Wl,-z,relro -Wl,-z,now -shared -Wl,-soname,libnative-lib.so -o ../../../../build/intermediates/cmake/debug/obj/mips64/libnative-lib.so CMakeFiles/native-lib.dir/src/main/cmake/native-lib.c.o  ../../../../src/main/cpp/libTest.a -lm && :
/Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/toolchains/mips64el-linux-android-4.9/prebuilt/darwin-x86_64/lib/gcc/mips64el-linux-android/4.9.x/../../../../mips64el-linux-android/bin/ld: unknown architecture of input file `../../../../src/main/cpp/libTest.a(test.o)' is incompatible with mips:isa64r6 output
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.

又失敗了,有點崩潰。。貌似從標紅處看是不支持相應的架構,此時想起來在build.gradle中打包APK時須要指定CPU類型,因此能夠嘗試加一下:

再編譯,終於成功了:

其中須要注意這個:

其實還有一個變量用得比較多,以下:

編譯看一下它的輸出:

那這個值是從哪獲取的呢?其實就是咱們在build.gradle中配置的,以下:

若是我們再加一個CPU架構,再看一下輸出效果:

 

上面是連接的靜態庫的配置,下面來使用動態庫來連接,那又如何配呢?這裏須要注意:須要將咱們的動態.so庫放到jniLibs裏面,不然不會打包進apk,具體以下:

而後修改CMakeLists.txt配置:

此時,我們在Android4.3機器上運行一下:

11-30 09:42:22.583 5731-5731/? E/AndroidRuntime: FATAL EXCEPTION: main
    java.lang.UnsatisfiedLinkError: dlopen failed: could not load library "../../../../src/main/jniLibs/armeabi-v7a/libTest.so" needed by "libnative-lib.so"; caused by library "../../../../src/main/jniLibs/armeabi-v7a/libTest.so" not found
        at java.lang.Runtime.loadLibrary(Runtime.java:362)
        at java.lang.System.loadLibrary(System.java:525)
        at com.jni.test.MainActivity.<clinit>(MainActivity.java:9)
        at java.lang.Class.newInstanceImpl(Native Method)
        at java.lang.Class.newInstance(Class.java:1130)
        at android.app.Instrumentation.newActivity(Instrumentation.java:1078)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2223)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2362)
        at android.app.ActivityThread.access$700(ActivityThread.java:168)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1329)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:177)
        at android.app.ActivityThread.main(ActivityThread.java:5493)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:525)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1225)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1041)
        at dalvik.system.NativeStart.main(Native Method)

其實跟用.mk引入預編譯動態庫同樣,6.0以前的系統是不會自動加入依賴庫的,因此我們須要手動加載一下依賴庫,以下:

此時再運行就正常了,而後apk中就有了兩個so文件,以下:

那若是改運行到Android6.0以上的機型,運行:

可是和mk不一樣的是,CMake有其它辦法引入動態庫讓它運行在Android6.0以上也沒問題,如何整:

試一下:

再運行,在全部版本均可運行了。

關於.mk和cmake引入預編譯動態庫在Android5.0及如下與6.0及以上的注意事項這裏總結一下,以勉踩坑:​

好比存在兩個動態庫libhello-jni.so 與 libTest.solibhello-jni.so依賴於libTest.so (使用NDK下的ndk-depends可查看依賴關係),則:

其中對於Android.mk來講: 使用Android.mk在 >=6.0 設備上不能再使用預編譯動態庫(靜態庫沒問題):

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := Test
#libTest.so放在當前文件同目錄
LOCAL_SRC_FILES := libTest.so
#預編譯庫
include $(PREBUILT_SHARED_LIBRARY)


include $(CLEAR_VARS)
#引入上面的Test模塊
LOCAL_SHARED_LIBRARIES := Test
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
include $(BUILD_SHARED_LIBRARY)

上面這段配置生成的libhllo-jni在>=6.0設備中沒法執行。

而對於CMake來講:使用CMakeList.txt在 >=6.0 設備上引入預編譯動態庫配置以下:

cmake_minimum_required(VERSION 3.4.1)

file(GLOB SOURCE *.c )
add_library(
             hello-jni
             SHARED
            ${SOURCE} )
#這段配置在6.0依然沒問題 
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -L[SO所在目錄]")

#這段配置只能在6.0如下使用 緣由和android.mk同樣
#add_library(Test SHARED IMPORTED)
#set_target_properties(Test PROPERTIES IMPORTED_LOCATION [SO絕對地址])

target_link_libraries(  hello-jni Test )

好,接下來看另一個CMake的東東:若是說咱們的代碼中要使用Android的Log庫,以前我們也說過,它的實現是處於NDK的這塊位置:

我們來看一下在CMake中來如何指定這個動態庫,先在源代碼中增長一個LOG的代碼:

目前沒有配置的話編譯確定會拋異常,以下:

Build command failed.
Error while executing process /Users/xiongwei/android-sdks/Android/sdk/cmake/3.6.3155560/bin/cmake with arguments {--build /Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/.externalNativeBuild/cmake/debug/armeabi-v7a --target native-lib}
[1/2] Building C object CMakeFiles/native-lib.dir/src/main/cmake/native-lib.c.o
clang: warning: argument unused during compilation: '-L/Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/src/main/jniLibs/armeabi-v7a'
[2/2] Linking C shared library ../../../../build/intermediates/cmake/debug/obj/armeabi-v7a/libnative-lib.so
FAILED: : && /Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang  --target=armv7-none-linux-androideabi --gcc-toolchain=/Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64 --sysroot=/Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/platforms/android-14/arch-arm -fPIC -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -fno-integrated-as -mthumb -Wa,--noexecstack -Wformat -Werror=format-security  -L/Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/src/main/jniLibs/armeabi-v7a -O0 -fno-limit-debug-info  -Wl,--build-id -Wl,--warn-shared-textrel -Wl,--fatal-warnings -Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,-z,noexecstack -Qunused-arguments -Wl,-z,relro -Wl,-z,now -shared -Wl,-soname,libnative-lib.so -o ../../../../build/intermediates/cmake/debug/obj/armeabi-v7a/libnative-lib.so CMakeFiles/native-lib.dir/src/main/cmake/native-lib.c.o  -lTest -lm && :
/Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/src/main/cmake/native-lib.c:11: error: undefined reference to '__android_log_print'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.

下面來配置一下,這裏就會學習到用查找的方式來將這個系統日誌的so的路徑給查出來,而不是手動去寫死,以下:

我們來看一下打印:

運行看一下是否打印出來日誌了:

可是!!其實上面在CMake引入log庫的方式還能夠簡化,以下:

另外還有一點就是我們目前的源文件只有一個,這樣配置沒啥問題:

那若是有不少源文件一個個這樣添加是否是很麻煩,這時能夠引用某個目錄下的全部源文件,以下:

另外還有一種指定目錄的方式,瞭解下:

若是在cmake中須要使用其餘目錄的cmakelist,能夠這樣,具體就不演示了:

另外對於頭文件的包含還有個小細節須要注意下,先新建一個頭文件:

若是源文件中想要引用這個頭文件,能夠這樣寫:

可是還能夠在CMake中加入這樣一個配置來支持這樣的寫法,以下:

具體配置以下:

其實這句話配置就至關於:

此時再編譯源文件就沒報紅了:

至此關於.mk和cmake相關配置的東東就學到這了,比上次學習還雜,可是收穫仍是頗多滴~~

相關文章
相關標籤/搜索