天天看點

使用CMake來進行Android NDK開發

本篇文章已授權微信公衆号 guolin_blog (郭霖)獨家釋出

前言

Android NDK開發可能在平時的項目開發中不常用到,但是這并不代表其不重要,  
相反NDK開發是Android開發人員的進階過程中必須要掌握的技能。  
Android NDK是一組允許将C或C++(原生代碼)嵌入到Android應用中的工具。  
如果開發者在需要以下操作的時候,使用NDK開發特别有用:  
*  在平台之間移植其應用
*  從裝置擷取卓越性能以用于計算密集型應用,例如遊戲或實體模拟。  
*  重複使用現有庫或者提供自己庫供重複使用。  
除此之外,對于ndk的學習,也有助于加深開發者在閱讀架構的源碼了解。  
ndk開發有兩種編譯方式,一種是通過ndk-build來建構;  
一種是通過CMake建構原生庫。通過CMake建構原生庫是Google新提出來的方式,比較友善、強大。  
           

準備

通過cmake進行ndk開發首先有個要求,需要Android Studio的版本是2.2以上版本(包含2.2)  
,Gradle的版本需要升到2.2.0及以上。  
滿足上面的條件下,我們需要下載下傳ndk和建構工具。如下圖:  
           
使用CMake來進行Android NDK開發
紅線标記的三個工具下載下傳好就行。CMake和NDK就不說了,都好了解,  
LLDB呢是一種調試程式,用來調試原生代碼的。  
           

向項目添加原生代碼

上面的準備工作做完之後就可以向項目中添加原生代碼,建構原生庫進行ndk開發了。  
這邊有個讨論,向項目中添加原生代碼有兩種情況:  
一種是建立一個新的項目支援C/C++;  
一種是在已有項目中添加原生代碼。  
是以這邊相應的也有兩種方式。先說第一種方式。  
           

1.建立支援C/C++的新項目

建立支援原生代碼的新項目跟平常開發中建立一個新項目沒有太大的差別,說一下不同的地方。  
1.前者在向導的Config your new project界面需要選中  
 Include C++ Support 複選框。如圖1:
           
使用CMake來進行Android NDK開發
2.前者在向導的Customize C++ Support 界面會有C++ Standard 的選擇  
意思是你希望使用哪種 C++ 标準。  
選擇 Toolchain Default 會使用預設的 CMake 設定。  
這裡我們選擇預設的。  
Exceptions Support:如果你希望啟用對 C++ 異常處理的支援,請選中此複選框。  
如果啟用此複選框,Android Studio 會将 -fexceptions 标志添加到子產品級 build.gradle 檔案的   
cppFlags 中,Gradle 會将其傳遞到 CMake。  
Runtime Type Information Support:如果你希望支援 RTTI,請選中此複選框。  
如果啟用此複選框,Android Studio 會将 -frtti 标志添加到子產品級 build.gradle 檔案的   
cppFlags 中,Gradle 會将其傳遞到 CMake。  
這裡自己看需求選擇勾不勾選,這邊示範的demo選擇勾選。如圖2:  
           
使用CMake來進行Android NDK開發
建立項目完成之後,在as的左側項目結構目錄中的app應用子產品中可以看到cpp檔案夾,  
cpp檔案件裡面存放有屬于項目的所有原生源檔案、标頭和預建構庫。  
對于新項目,Android Studio 會建立一個示例 C++ 源檔案 native-lib.cpp  
除了cpp檔案夾之外,我們還看到一個CMakeLists.txt的這樣一個檔案。  
這個檔案是CMake的建構腳本,下面會詳細說,這裡就暫且不說。  
之前有說過建立支援C/C++的項目會提供了一個示例的c++源檔案native-lib.cpp,存放在cpp檔案夾中  
,我們點開看看内容。
           
使用CMake來進行Android NDK開發
恩,是使用C++寫的一個方法,傳回一個"Hello from C++",我們跟蹤進去這個方法,看看哪裡調用。
           
使用CMake來進行Android NDK開發
我們發現,這個原生方法是在MainActivity中調用的,傳回的字元串設定給了TextView。  
通過這兩張圖我們發現,原生方法通過native關鍵字來表示、在使用原生庫之前,  
需要通過System.loadLibrary(“庫名稱”)加載、  
原生方法的方法名命名等常見要點。這邊就一筆略過。因為本文重點是CMake建構原生庫。  
是以ndk的一些基礎知識,以及JNI等,就不細說。本文假設讀者知道這些。  
這樣的一個新的支援原生代碼的項目建立之後,并且還自帶demo,開發者就可以很友善在上面進行開發。  
假設現在要建立一個原生庫進行ndk開發,我們在cpp檔案夾下new一個C/C++ Source file ,  
在你new出來的檔案編寫你的C/C++代碼邏輯,然後在CMakeLists.txt配置檔案上稍作配置,即可。  
下面會詳述CMakeLists.txt的配置。  
           

2.向現有項目添加原生代碼

向現有項目添加原生代碼,進行ndk開發,這裡選擇一個我的項目,空閑時間寫的一個APP,  
一款仿今日頭條的資訊類軟體(求star)-
           

PalmRead

主要有三個大步驟。第一步:建立新的原生源檔案;第二步:建立CMake建構腳本;第三步:将Gradle關聯  
到原生庫。    
現在我們來看第一步,建立新的原生源檔案,首先我們要先在應用子產品src/main 目錄下  
建立一個cpp檔案夾用來存放原生源檔案等。  
然後在cpp檔案夾下面建立C/C++源檔案New > C/C++ Source File。如圖:
           
使用CMake來進行Android NDK開發
建立了一個名為palmread-lib檔案。  
第二步,我們建立CMake建構腳本,也就是CMakeLists.txt檔案,在應用子產品下new一個file檔案,  
命名為CMakeLists.txt即可,  
注意哦,這個檔案的名稱不能搞錯哦。CMakeLists.txt建立好打開後,發現沒有任何内容,這就對了。。  
需要我們自己配置的。  
不像是通過第一種新建立支援原生代碼的項目那種,還給你寫好,不過我們可以參考第一種方式下,系統給我們  
預設生成的内容。  
咱們看一下第一種方式下,系統預設生成的CMakeLists.txt檔案的内容是什麼樣子吧。如圖:  
           
使用CMake來進行Android NDK開發
使用CMake來進行Android NDK開發
通過圖檔我們看到,其實内容也沒什麼。就cmake_minimum_required()、add_library()  
、find_library、target_link_libraries()。這幾個CMake指令,每個CMake指令都有英文介紹很好了解。  
比如add_library建立和命名一個庫,這邊名稱我們就填palmread-lib,種類分為static和shared,  
具體差別嘛移步:
           

static和shared的差別

這裡我們選擇SHARED,然後就是提供一個library的相對路徑。隻有在CMakeLists.txt檔案中配置了該指令  
,才能找到編譯這個庫。  
其他的一些CMake指令大家自行搜尋了解,都不是很難,很好了解,這裡篇幅有限就不細說了。  
那現在我們可以按照系統提供的模闆,基于自己的項目寫了一份CMakeLists.txt檔案,代碼如下:  
           
使用CMake來進行Android NDK開發

随後在Java類中加載palmread-lib庫,這裡我這邊寫了一個NdkHelper類,專門用來定義native方法的:

使用CMake來進行Android NDK開發

這一步做完後,第二步也算告一段落了,現在我們來看第三步:将 Gradle 關聯到你的原生庫。

将gradle關聯到原生庫有兩種方式,這裡就先說第一種比較簡單的方式,第二種下篇博文再說。

第一種方式是通過as的快捷鍵來實作的,右鍵點選你想要關聯到原生庫的子產品(例如 app 子產品),

并從菜單中選擇 Link C++ Project with Gradle。

BuildSystem選擇CMake,projectpath就是CMakeLists.txt檔案的路徑。點選ok完成。

你會在應用子產品的build.gradle檔案中發現,android閉包裡出現了

externalNativeBuild {
        cmake {
            path 'CMakeLists.txt'
        }
    }
           

這個語句就跟我們之前說的第二種方式有關系,這裡先不說了,後面博文再說。

同步項目後,突然想到之前建立的palmread-lib.c檔案好像還沒有寫C代碼呢,呀,這腦子,沒事,我們現在寫。  
寫些什麼呢?這樣吧我們用C來實作這樣一個功能,接受一個字元串參數,然後對字元串進行拼接修改操作  
,使得傳回一個新的字元串。  
代碼如下:
           
使用CMake來進行Android NDK開發
代碼寫好之後,就是去調用了,為了友善測試,我們選在在應用的首頁activity去調用這個原生方法。  
通過toast顯示方法傳回的值。  
如圖:
           
使用CMake來進行Android NDK開發
通過代碼,我們可以看出,我們調用了,NdkHelper.GetStringFromC()這個原生方法,  
傳入一個“歡迎來到PalmRead”字元串作為參數。按照之前的C邏輯,應該會傳回一個新的字元串為  
“歡迎來到PalmRead from c”。  
那我們運作程式試試效果吧。  
           
使用CMake來進行Android NDK開發
O(∩_∩)O哈!,我們發現toast顯示的值确實是我們設想的傳回值。這樣就架起了Java與原生代碼之間的橋梁。  
現在我們可以應用子產品的build/outputs/apk目錄下,打開我們的apk,我們會發現,我們建立的原生檔案,  
編譯成了原生庫"libpalmread-lib.so"也打包進了apk檔案。如圖:  
           
使用CMake來進行Android NDK開發

結語

好了,關于CMake來進行Android NDK開發,大緻的流程就是這樣。CMakeLists的一些指令 還有Gradle關聯原生庫的第二種通過編輯build.gradle檔案的方式 , 通過CMake來進行ndk開發之補充篇 有詳細說明。現在,總覽全貌,整體下來是不是比ndk-build友善很多呢。

掃碼體驗小程式:微捷徑
使用CMake來進行Android NDK開發

繼續閱讀