標籤:nat 上層 format att backtrace crash oge written 依賴
# 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-lib
,SHARED
表示編譯產生的是動態連結程式庫
(這個概念前面已經提到過了),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>
上面是列印日誌的標頭檔,必須添加
(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;
上面是一個null 指標異常,我們運行程式,發現崩潰了,然後查看控制台,只有下面一行資訊:
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 0x0I/DEBUG ( 67): eax 00000000 ebx f3494fcc ecx ffa881a0 edx 00000000I/DEBUG ( 67): esi f434e2b0 edi 00000000I/DEBUG ( 67): xcs 00000023 xds 0000002b xes 0000002b xfs 00000007 xss 0000002bI/DEBUG ( 67): eip f3492a06 ebp ffa88318 esp ffa88280 flags 00210246I/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/[email protected]@com.codekong.ndkdemo1-[email protected]@classes.dexI/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 0x0Stack frame I/DEBUG ( 67): #00 pc 00000a06 /data/app/com.codekong.ndkdemo1-2/lib/x86/libHelloNDK.so (Java_com_codekong_ndkdemo1_MainActivity_updateFile+150): Routine Java_com_codekong_ndkdemo1_MainActivity_updateFile at F:\AndroidFirstCode\NDKDemo1\app\src\main\cpp/HelloJNI.c:32Stack frame I/DEBUG ( 67): #01 pc 0026e27b /data/dalvik-cache/x86/[email protected]@com.codekong.ndkdemo1-[email protected][email protected].dexStack frame I/DEBUG ( 67): #02 pc 9770ee7d <unknown>: Unable to open symbol file app/build/intermediates/cmake/debug/obj/x86/<unknown>. Error (22): Invalid argumentStack frame I/DEBUG ( 67): #03 pc a4016838 <unknown>: Unable to open symbol file app/build/intermediates/cmake/debug/obj/x86/<unknown>. Error (22): Invalid argumentCrash dump is completed
尤其是上面的一句:
g_ndkdemo1_MainActivity_updateFile+150): Routine Java_com_codekong_ndkdemo1_MainActivity_updateFile at F:\AndroidFirstCode\NDKDemo1\app\src\main\cpp/HelloJNI.c:32
準確指出了發生錯誤的行數
AndroidStudio項目CMakeLists解析