使用Android Studio和CMake進(jìn)行NDK開發(fā) - 基礎(chǔ)

1. 概述


在Android Studio 2.2之后,可以使用CMake來進(jìn)行NDK開發(fā),C/C++開發(fā)的便利性又提升了不少。這個(gè)是個(gè)好事,比較CMake使用起來還是比make要簡(jiǎn)單,并且抽象、跨平臺(tái)。例如在linux可以生產(chǎn)linux下的makefile,在windows下可以生產(chǎn)Visual Studio的工程文件。

這里需要解析幾個(gè)名詞:

  • NDK

Android Native Development Kit,里面包含各個(gè)平臺(tái)上的C/C++編譯器、相關(guān)頭文件和庫(kù)(相當(dāng)于Java的庫(kù))。

  • CMake

一套構(gòu)建系統(tǒng),類似Gradle,但是CMake不直接參與編譯,而是產(chǎn)生其他構(gòu)建系統(tǒng)的工程文件,再進(jìn)行編譯,在Android Studio當(dāng)中,Gradle插件會(huì)驅(qū)動(dòng)CMake產(chǎn)生各個(gè)平臺(tái)(armeabi、armeabi-v7a、x86等)的ninja的構(gòu)建文件,再驅(qū)動(dòng)編譯器進(jìn)行編譯。

  • LLDB

調(diào)試器,可以用來調(diào)試原生代碼,之前版本使用的是GDB。

2. 準(zhǔn)備


本文是基于Android Studio 2.3來講解的,因此需要升級(jí)到2.3。安裝好2.3之后,打開【SDK Manager】

image.png

勾選:【CMake】和【NDK】?jī)蓚€(gè)選項(xiàng),然后點(diǎn)擊【Apply】進(jìn)行安裝

因?yàn)間oogle在國(guó)內(nèi)假設(shè)了鏡像站點(diǎn),現(xiàn)在不需要使用[可不描述]來更新SDK了

3. 實(shí)踐

3.1 創(chuàng)建項(xiàng)目


創(chuàng)建項(xiàng)目的流程,官方文檔也有: https://developer.android.com/studio/projects/add-native-code.html
因此,我這里會(huì)在流程上補(bǔ)充一些說明。

新建項(xiàng)目,在第一頁(yè)中勾選:

image.png

【include c++ support】來支持C++開發(fā),如果已有項(xiàng)目沒有勾選也沒關(guān)系,可以在菜單中【link 】

一路next來到最后一頁(yè),定制你的C++項(xiàng)目支持

image.png

主要有三個(gè)參數(shù)組成:

  • C++標(biāo)準(zhǔn)

現(xiàn)在基本都是C++ 11開發(fā)了,如果不是要維護(hù)非常老的代碼,建議選擇C++11。
C++11相對(duì)于之前的版本(C++03)增加的功能非常豐富,具體可以參考這篇文章: http://blog.csdn.net/zhuxianjianqi/article/details/8658169

  • Exception Support

異常支持,如果取消掉的話,那么就不能使用 try-catch 進(jìn)行異常處理了,建議選擇。

  • Runtime Type Information Support

運(yùn)行時(shí)類型信息支持,在C++運(yùn)行的時(shí)候,不像Java、C#等一樣,可以動(dòng)態(tài)獲取對(duì)象的類信息,開啟這個(gè)選項(xiàng)來支持這個(gè)功能,建議選擇。

到這里,項(xiàng)目就創(chuàng)建完畢了,點(diǎn)擊 run 按鈕,APP就可以在模擬器或者android設(shè)備上運(yùn)行。

3.2 工程結(jié)構(gòu)


打開代碼所在的目錄,進(jìn)入APP子模塊,可以看到相比傳統(tǒng)的APP項(xiàng)目,會(huì)多出以下文件或者目錄:

  • CMakeList.txt

CMake的工程文件,相當(dāng)于 build.gradle 用于說明編譯那些C/C++源碼,以及相關(guān)的編譯參數(shù)

  • src/main/cpp

C/C++源碼目錄

  • .externalNativeBuild

該文件夾是臨時(shí)文件夾,gradle插件會(huì)調(diào)用cmake產(chǎn)生各個(gè)平臺(tái)的臨時(shí)構(gòu)建文件,都存放在該目錄

3.3 編譯流程


需要注意的是,cmake并不能直接編譯 c/c++ 源碼,需要產(chǎn)生 ninja 的項(xiàng)目文件,才會(huì)編譯,其流程大體是這樣的。

生成ninja工程 ---> 編譯/鏈接 ---> APP打包

對(duì)于 產(chǎn)生ninja工程,可以通過下述三種方式:

  • 修改CMakeLists.txt,然后執(zhí)行 Build菜單的 Make Project 或者 Make module 或者直接 Run
  • 執(zhí)行 Gradle Sync
  • 執(zhí)行 Build菜單的 Refresh Linked C++ Projects

在實(shí)際使用中,有時(shí)候修改CMakeLists.txt不會(huì)重新產(chǎn)生ninja工程文件,導(dǎo)致編譯會(huì)出現(xiàn)問題,所以官方可能也留了 Refresh Linked C++ Projects 給到大家手動(dòng)刷新。另外,手動(dòng)添加 C/C++源碼的時(shí)候,也可以通過這種方式刷新工程。

4. 編譯參數(shù)解析

4.1 build.gradle


該文件可以指定工具鏈的大部分核心參數(shù),里面的源碼大致如下:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToolsVersion "25.0.3"
    defaultConfig {
        applicationId "com.test.ndkhelloworld"

        ...

        // 該代碼塊用于配置相關(guān)的參數(shù)
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11 -frtti -fexceptions"
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    // 該代碼塊用于鏈接到指定的CMakeLists.txt,路徑是相對(duì)路徑
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

...

這里有兩個(gè) externalNativeBuild 代碼塊

  • android.externalNativeBuild

ExternalNativeBuild 對(duì)象:http://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.ExternalNativeBuild.html
主要配置 cmake 或者 ndk-build 的工程文件路徑

  • `android.defaultConfig.externalNativeBuild

ExternalNativeBuildOptions 對(duì)象: http://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.ExternalNativeBuild.html
具體的參數(shù)配置。后面都是以這個(gè)配置參數(shù)來講解。

4.2 CMake變量解析


可以通過 arguments 命令來傳遞CMake構(gòu)建參數(shù)(這些參數(shù),實(shí)際會(huì)傳遞到NDK的構(gòu)建工具鏈),形式為: -D參數(shù)名=參數(shù)值1 參數(shù)值2,需要注意的是,如果有多個(gè)參數(shù),那么必須換行來傳遞,例如:

externalNativeBuild {
            cmake {
                arguments "-DANDROID_TOOLCHAIN=gcc"
                            "-DANDROID_STL=stlport_static"
            }
        }

以下是常用的變量:

  • ANDROID_TOOLCHAIN

編譯工具鏈,可選:clang(默認(rèn))和gcc(已經(jīng)過期)。

  • ANDROID_PLATFORM

android平臺(tái),例如:android-18 注意,該取值會(huì)影響到原生API的時(shí)候,有些原生API在低版本的android是沒有的,詳見: https://developer.android.com/ndk/guides/stable_apis.html

  • ANDROID_STL

STL(標(biāo)準(zhǔn)模板庫(kù))的選擇,NDK自帶了很多個(gè)版本的STL,功能大體上是一樣的,但是授權(quán)會(huì)不一樣。詳細(xì)請(qǐng)閱讀: https://developer.android.com/ndk/guides/cpp-support.html#hr
(stlport已經(jīng)實(shí)現(xiàn)異常處理了,在低版本的NDK是不支持的)

  • ANDROID_PIE

啟用PIE(position-independent executables),如果是 ANDROID_PLATFORM = android-16,該選項(xiàng)默認(rèn)開啟(取值為 ON),否則為 OFF

  • ANDROID_CPP_FEATURES

C++的功能,取值為: rttiexceptions,也可以使用 cppFlags 指定

  • ANDROID_ALLOW_UNDEFINED_SYMBOLS

允許未定義的符號(hào),默認(rèn)為 FALSE,可以取值為:TRUE。

  • ANDROID_ARM_MODE

指令集模式,默認(rèn)為: thumb ,可以取值為: arm

  • ANDROID_ARM_NEON

是否啟用NEON,默認(rèn)為FALSE,啟用為:TRUE

  • ANDROID_DISABLE_FORMAT_STRING_CHECKS

是否禁用字符串格式化檢查,默認(rèn)為:FALSE,可以取值為:TRUE,建議默認(rèn)值,不要禁用,因?yàn)楹芏嗦┒椿蛘連UG都出現(xiàn)在字符串格式化上面。

4.3 abi過濾器


通過 abiFilters 可以編譯出指定ABI的二進(jìn)制文件,可選值為:armeabiarmeabi-v7a、arm64-v8a、mips、mips64x86以及x86_64

默認(rèn)情況下,編譯出來的都包含上述的ABI的二進(jìn)制文件,如下圖:

APK信息,build菜單->Analyze APK->選擇產(chǎn)生的APK

可以清楚的看到,編譯出來的二進(jìn)制文件(庫(kù))可以在ARM、X86和MIPS所有平臺(tái)上運(yùn)行。實(shí)際上,我們想給APK瘦身的,不需要在那么多平臺(tái)上運(yùn)行,可以取消掉一些平臺(tái)的支持,例如我們只支持armeabi和armeabi-v7a,X86和MIPS都不需要

externalNativeBuild {
            cmake {
                abiFilters "armeabi", "armeabi-v7a"
                cppFlags "-std=c++11 -frtti -fexceptions"
                arguments "-DANDROID_TOOLCHAIN=gcc",
                            "-DANDROID_STL=stlport_static"
            }
        }

執(zhí)行clean之后再產(chǎn)生APK,可以看到,只有兩個(gè)ABI的二進(jìn)制產(chǎn)生

APK信息,build菜單->Analyze APK->選擇產(chǎn)生的APK

我發(fā)現(xiàn)一個(gè)問題,即使sync、clean等一系列的操作后,不會(huì)刪除原有產(chǎn)生的ninja工程文件,可以先手動(dòng)刪除掉 .externalNativeBuild 目錄再重試一下。

4.4 傳遞C/C++參數(shù)


cFlags和cppFlags可以傳遞C/C++編譯參數(shù),這里不詳細(xì)討論。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容