AndroidStudio項目CMakeLists解析


# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-lib.cpp )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

 

上面的完成的有注釋的內容,但其中最核心的也就幾句,下面分別做介紹:

cmake_minimum_required(VERSION 3.4.1) 用來設置在編譯本地庫時我們需要的最小的cmake版本,AndroidStudio自動生成,我們幾乎不需要自己管。

add_library( # Sets the name of the library. native-lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). src/main/cpp/native-lib.cpp )

add_library用來設置編譯生成的本地庫的名字為native-libSHARED表示編譯生成的是動態鏈接庫(這個概念前面已經提到過了),src/main/cpp/native-lib.cpp表示參與編譯的文件的路徑,這里面可以寫多個文件的路徑。

find_library 是用來添加一些我們在編譯我們的本地庫的時候需要依賴的一些庫,由於cmake已經知道系統庫的路徑,所以我們這里只是指定使用log庫,然后給log庫起別名為log-lib便於我們后面引用,此處的log庫是我們后面調試時需要用來打log日志的庫,是NDK為我們提供的。

target_link_libraries 是為了關聯我們自己的庫和一些第三方庫或者系統庫,這里把我們把自己的庫native-lib庫和log庫關聯起來。

NDK自定義配置

1 . 添加多個參與編譯的C/C++文件

首先,我們發現我們上面的例子都是涉及到一個C++文件,那么我們實際的項目不可能只有一個C++文件,所以我們首先要改變CMakeLists.txt文件,如下 :

add_library( HelloNDK
             SHARED
             src/main/cpp/HelloNDK.c src/main/cpp/HelloJNI.c)

簡單吧,簡單明了,但是這里要注意的是,你在寫路徑的時候一定要注意當前的CMakeLists.txt在項目中的位置,上面的路徑是相對於CMakeLists.txt 寫的。

2 . 我們想編譯出多個so庫

大家會發現,我們上面這樣寫,由於只有一個CMakeLists.txt文件,所以我們會把所有的C/C++文件編譯成一個so庫,這是很不合適的,這里我們就試着學學怎么編譯出多個so庫。

先放上我的項目文件夾結構圖:

文件夾結構

然后看看我們每個CMakeLists.txt文件是怎么寫的:

one文件夾內的CMakeLists.txt文件的內容:

ADD_LIBRARY(one-lib SHARED one-lib.c) target_link_libraries(one-lib log)

two文件夾內的CMakeLists.txt文件的內容:

ADD_LIBRARY(two-lib SHARED two-lib.c) target_link_libraries(two-lib log)

app目錄下的CMakeLists.txt文件的內容

# Sets the minimum version of CMake required to build the native library. cmake_minimum_required(VERSION 3.4.1) add_library( HelloNDK SHARED src/main/cpp/HelloNDK.c src/main/cpp/HelloJNI.c) find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log ) target_link_libraries(HelloNDK log) ADD_SUBDIRECTORY(src/main/cpp/one) ADD_SUBDIRECTORY(src/main/cpp/two)

通過以上的配置我們可以看出CMakeLists.txt 文件的配置是支持繼承的,所以我們在子配置文件中只是寫了不同的特殊配置項的配置,最后在最上層的文件中配置子配置文件的路徑即可,現在編譯項目,我們會在 <項目目錄>\app\build\intermediates\cmake\debug\obj\armeabi 下面就可以看到生成的動態鏈接庫。而且是三個動態鏈接庫

3 . 更改動態鏈接庫生成的目錄

我們是不是發現上面的so庫的路徑太深了,不好找,沒事,可以配置,我們只需要在頂層的CMakeLists.txt文件中加入下面這句就可以了

#設置生成的so動態庫最后輸出的路徑 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})

然后我們就可以在app/src/main下看到jniLibs目錄,在其中看到我們的動態鏈接庫的文件夾和文件(這里直接配置到了系統默認的路徑,如果配置到其他路徑需要在gradle文件中使用jinLibs.srcDirs = ['newDir']進行指定)。

NDK錯誤調試

在開發的過程中,難免會遇到bug,那怎么辦,打log啊,下面我們就談談打log和看log的姿勢。

1 . 在C/C++文件中打log

(1) 在C/C++文件中添加頭文件

#include <android/log.h>
  • 1

上面是打印日志的頭文件,必須添加

(2) 添加打印日志的宏定義和TAG

//log定義 #define LOG "JNILOG" // 這個是自定義的LOG的TAG #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG,__VA_ARGS__) // 定義LOGD類型 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG,__VA_ARGS__) // 定義LOGI類型 #define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG,__VA_ARGS__) // 定義LOGW類型 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG,__VA_ARGS__) // 定義LOGE類型 #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG,__VA_ARGS__) // 定義LOGF類型

上面的日志級別和Android中的log是對應的。

(3) 經過上面兩步,我們就可以打印日志啦

int len = 5; LOGE("我是log %d", len);

現在我們就可以在logcat中看到我們打印的日志啦。

2 . 查看報錯信息

首先我們先手動寫一個錯誤,我們在上面的C文件中找一個函數,里面寫入如下代碼:

int * p = NULL; *p = 100;

上面是一個空指針異常,我們運行程序,發現崩潰了,然后查看控制台,只有下面一行信息:

libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 17481

完全看不懂上面的信息好吧,這個也太不明顯了,下面我們就學習一下如何將上面的信息變得清楚明了

我們需要用到是ndk-stack工具,它在我們的ndk根目錄下,它可以幫助我們把上面的信息轉化為更為易懂更詳細的報錯信息,下面看看怎么做:

(1) 打開AndroidStudio中的命令行,輸入adb logcat > log.txt

上面這句我們是使用adb命令捕獲log日志並寫入log.txt文件,然后我們就可以在項目根目錄下看到log.txt文件

(2) 將log.txt打開看到報錯信息,如下:

F/libc    (17481): Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 17481 (dekong.ndkdemo1) I/DEBUG ( 67): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** I/DEBUG ( 67): Build fingerprint: 'generic/vbox86p/vbox86p:5.0/LRX21M/genymotion08251046:userdebug/test-keys' I/DEBUG ( 67): Revision: '0' I/DEBUG ( 67): ABI: 'x86' I/DEBUG ( 67): pid: 17481, tid: 17481, name: dekong.ndkdemo1 >>> com.codekong.ndkdemo1 <<< I/DEBUG ( 67): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 I/DEBUG ( 67): eax 00000000 ebx f3494fcc ecx ffa881a0 edx 00000000 I/DEBUG ( 67): esi f434e2b0 edi 00000000 I/DEBUG ( 67): xcs 00000023 xds 0000002b xes 0000002b xfs 00000007 xss 0000002b I/DEBUG ( 67): eip f3492a06 ebp ffa88318 esp ffa88280 flags 00210246 I/DEBUG ( 67): I/DEBUG ( 67): backtrace: I/DEBUG ( 67): #00 pc 00000a06 /data/app/com.codekong.ndkdemo1-2/lib/x86/libHelloNDK.so (Java_com_codekong_ndkdemo1_MainActivity_updateFile+150) I/DEBUG ( 67): #01 pc 0026e27b /data/dalvik-cache/x86/data@app@com.codekong.ndkdemo1-2@base.apk@classes.dex I/DEBUG ( 67): #02 pc 9770ee7d <unknown> I/DEBUG ( 67): #03 pc a4016838 <unknown> I/DEBUG ( 67): I/DEBUG ( 67): Tombstone written to: /data/tombstones/tombstone_05

現在的報錯信息還是看不懂,所以我們需要使用ndk-stack轉化一下:

(3) 繼續在AndroidStudio中的命令行中輸入如下命令(在這之前,我們必須要將ndk-stack的路徑添加到環境變量,以便於我們在命令行中直接使用它)

ndk-stack -sym app/build/intermediates/cmake/debug/obj/x86 -dump ./log.txt

上面的-sym后面的參數為你的對應平台(我是Genymotion模擬器,x86平台)的路徑,如果你按照上面的步驟改了路徑,那就需要寫改過的路徑,-dump后面的參數就是我們上一步得出的log.txt文件,執行結果如下:

********** Crash dump: ********** Build fingerprint: 'generic/vbox86p/vbox86p:5.0/LRX21M/genymotion08251046:userdebug/test-keys' pid: 17481, tid: 17481, name: dekong.ndkdemo1 >>> com.codekong.ndkdemo1 <<< signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 Stack frame I/DEBUG ( 67): #00 pc 00000a06 /data/app/com.codekong.ndkdemo1-2/lib/x86/libHelloNDK.so (Java_com_codekon g_ndkdemo1_MainActivity_updateFile+150): Routine Java_com_codekong_ndkdemo1_MainActivity_updateFile at F:\AndroidFirstCode\NDK Demo1\app\src\main\cpp/HelloJNI.c:32 Stack frame I/DEBUG ( 67): #01 pc 0026e27b /data/dalvik-cache/x86/data@app@com.codekong.ndkdemo1-2@base.apk@classes.d ex Stack frame I/DEBUG ( 67): #02 pc 9770ee7d <unknown>: Unable to open symbol file app/build/intermediates/cmake/debug/ obj/x86/<unknown>. Error (22): Invalid argument Stack frame I/DEBUG ( 67): #03 pc a4016838 <unknown>: Unable to open symbol file app/build/intermediates/cmake/debug/ obj/x86/<unknown>. Error (22): Invalid argument Crash dump is completed

尤其是上面的一句:

g_ndkdemo1_MainActivity_updateFile+150): Routine Java_com_codekong_ndkdemo1_MainActivity_updateFile at F:\AndroidFirstCode\NDK Demo1\app\src\main\cpp/HelloJNI.c:32

准確指出了發生錯誤的行數


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM