使用Android Studio和CMake進行NDK開發(fā) - 基礎

1. 概述


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

這里需要解析幾個名詞:

  • NDK

Android Native Development Kit,里面包含各個平臺上的C/C++編譯器、相關頭文件和庫(相當于Java的庫)。

  • CMake

一套構建系統(tǒng),類似Gradle,但是CMake不直接參與編譯,而是產生其他構建系統(tǒng)的工程文件,再進行編譯,在Android Studio當中,Gradle插件會驅動CMake產生各個平臺(armeabi、armeabi-v7a、x86等)的ninja的構建文件,再驅動編譯器進行編譯。

  • LLDB

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

2. 準備


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

image.png

勾選:【CMake】和【NDK】兩個選項,然后點擊【Apply】進行安裝

因為google在國內假設了鏡像站點,現(xiàn)在不需要使用[可不描述]來更新SDK了

3. 實踐

3.1 創(chuàng)建項目


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

新建項目,在第一頁中勾選:

image.png

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

一路next來到最后一頁,定制你的C++項目支持

image.png

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

  • C++標準

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

  • Exception Support

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

  • Runtime Type Information Support

運行時類型信息支持,在C++運行的時候,不像Java、C#等一樣,可以動態(tài)獲取對象的類信息,開啟這個選項來支持這個功能,建議選擇。

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

3.2 工程結構


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

  • CMakeList.txt

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

  • src/main/cpp

C/C++源碼目錄

  • .externalNativeBuild

該文件夾是臨時文件夾,gradle插件會調用cmake產生各個平臺的臨時構建文件,都存放在該目錄

3.3 編譯流程


需要注意的是,cmake并不能直接編譯 c/c++ 源碼,需要產生 ninja 的項目文件,才會編譯,其流程大體是這樣的。

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

對于 產生ninja工程,可以通過下述三種方式:

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

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

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

4.1 build.gradle


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

apply plugin: 'com.android.application'

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

        ...

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

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

...

這里有兩個 externalNativeBuild 代碼塊

  • android.externalNativeBuild

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

  • `android.defaultConfig.externalNativeBuild

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

4.2 CMake變量解析


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

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

以下是常用的變量:

  • ANDROID_TOOLCHAIN

編譯工具鏈,可選:clang(默認)和gcc(已經過期)。

  • ANDROID_PLATFORM

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

  • ANDROID_STL

STL(標準模板庫)的選擇,NDK自帶了很多個版本的STL,功能大體上是一樣的,但是授權會不一樣。詳細請閱讀: https://developer.android.com/ndk/guides/cpp-support.html#hr
(stlport已經實現(xiàn)異常處理了,在低版本的NDK是不支持的)

  • ANDROID_PIE

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

  • ANDROID_CPP_FEATURES

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

  • ANDROID_ALLOW_UNDEFINED_SYMBOLS

允許未定義的符號,默認為 FALSE,可以取值為:TRUE。

  • ANDROID_ARM_MODE

指令集模式,默認為: thumb ,可以取值為: arm

  • ANDROID_ARM_NEON

是否啟用NEON,默認為FALSE,啟用為:TRUE

  • ANDROID_DISABLE_FORMAT_STRING_CHECKS

是否禁用字符串格式化檢查,默認為:FALSE,可以取值為:TRUE,建議默認值,不要禁用,因為很多漏洞或者BUG都出現(xiàn)在字符串格式化上面。

4.3 abi過濾器


通過 abiFilters 可以編譯出指定ABI的二進制文件,可選值為:armeabi、armeabi-v7aarm64-v8a、mips、mips64、x86以及x86_64

默認情況下,編譯出來的都包含上述的ABI的二進制文件,如下圖:

APK信息,build菜單->Analyze APK->選擇產生的APK

可以清楚的看到,編譯出來的二進制文件(庫)可以在ARM、X86和MIPS所有平臺上運行。實際上,我們想給APK瘦身的,不需要在那么多平臺上運行,可以取消掉一些平臺的支持,例如我們只支持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之后再產生APK,可以看到,只有兩個ABI的二進制產生

APK信息,build菜單->Analyze APK->選擇產生的APK

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

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


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

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

相關閱讀更多精彩內容

友情鏈接更多精彩內容