Android原生庫構建的三種姿態

前言

自Android Studio 2.2發布之后,AS開始支持CMake構建工具編譯構建原生代碼庫,谷歌推薦使用CMake來構建新建的原生庫,不過為了兼顧老項目,AS還是支持ndk-build構建,除此之外,谷歌還推出了實驗性Gradle插件來構建原生庫,至此AS工具支持3種方式來構建原生庫。
實驗性Gradle插件由于依賴開發中的Gradle API,所以它是不穩定的,不過谷歌稱Android Studio團隊會繼續支持實驗性Gradle插件,谷歌希望將來該插件能取代當前的Gradle插件,因為其緊密集成的特性對于C/C++開發者更為便利,例如,更靈活的依賴管理。因此,對于想在IDE與構建系統之間建立最靈活的接口的開發者,可以嘗試使用實驗性Gradle插件。

這里主要介紹下谷歌推薦的方式—Cmake。

PS:以下內容主要來自谷歌官方文檔。

1、CMake

1.1、構建工具準備

  • 下載NDK:Android C/C++代碼工具集
  • 下載Cmake:外部構建工具
  • 下載LLDB:Android Studio 上面調試本地代碼的工具

使用 SDK Manager 來安裝上述組件:

  1. 打開一個項目,從菜單欄中選擇 Tools > Android > SDK Manager。
  2. 點擊 SDK Tools 選項卡。
  3. 勾選 LLDB,CMake 和 NDK。如圖:

1.2 創建 CMake 構建腳本

創建一個CMakeList.txt構建腳本文件,然后用CMake命令來配置腳本文件。
為了讓 CMake 將源代碼(native source code)編譯成 native library。需要在編譯文件中添加 cmake_minimum_required() 和 add_library() 命令:

# Sets the minimum version of CMake required to build your native library.
# This ensures that a certain set of CMake features is available to
# your build.

cmake_minimum_required(VERSION 3.4.1)

# Specifies a library name, specifies whether the library is STATIC or
# SHARED, and provides relative paths to the source code. You can
# define multiple libraries by adding multiple add.library() commands,
# and CMake builds them for you. When you build your app, Gradle
# automatically packages shared libraries with your APK.

add_library( # Specifies 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(),將一個源文件(source file)或庫添加到 CMake 構建腳本,同步項目,然后 Android studio 將關聯的頭文件也顯示了。然而,為了讓 CMake 在編譯時期能定位到頭文件,需要在 CMake 構建腳本中添加 include_directories() 命令,并指定頭文件路徑:

add_library(...)

# Specifies a path to native header files.
include_directories(src/main/cpp/include/)

1.3、添加 NDK APIs

Android NDK 提供了一些有用的 native APIs。將 NDK librarys 添加到 CMakeLists.txt 腳本文件中,就可以使用這些 API 了。

預編譯的 NDK librarys 已經存在在 Android 平臺中了,所以你不需要編譯它們,或者是將其打包到你的 APK 中。因為這些 NDK librarys 已經是 CMake 搜索路徑的一部分,你甚至不需要提供你本地安裝的 NDK 路徑。你只需要向 CMake 提供你想使用的 library 名字。

將 find_library() 命令添加到你的 CMake 構建腳本中,這樣就可以定位 NDK library 的位置,并將其位置存儲在一個變量之中。你可以在構建腳本的其他地方使用這個變量,來代指 NDK library。下面的示例代碼將 Android-specific log support library 的位置存儲到變量 log-lib 中:

find_library( # Defines the name of the path variable that stores the
              # location of the NDK library.
              log-lib

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

NDK 同樣也包含一些只包含源碼的 library,這些就需要你去編譯,然后鏈接到你的本地庫(native library)。你可以在 CMake 構建腳本中使用 add_library() 命令將源碼編譯進本地庫。這時就需要提供你的本地 NDK 安裝路徑,通常將該路徑保存在 ANDROID_NDK 變量中,這樣 Android Studio 可以自動為你識別

下面的命令告訴 CMake 去構建 android_native_app_glue.c,這個命令可以管理 NativeActivity 的生命周期以及點擊輸入,并將其導入靜態庫中,然后將其鏈接至 native-lib:

add_library( app-glue
             STATIC
             ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c )

# You need to link static libraries against your shared native library.
target_link_libraries( native-lib app-glue ${log-lib} )

1.4、添加其他的預編譯庫

添加預編譯庫和添加本地庫(native library)類似。由于預編譯庫是已經構建好的,你想就要使用 IMPORTED 標志去告訴 CMake ,你只需要將其導入到你的項目中即可:

add_library( imported-lib
             SHARED
             IMPORTED )

然后你需要使用 set_target_properties()
命令去指定庫的路徑,就像下面的代碼那樣。
一些庫會根據不同的 CPU 使用不同的包,或者是 Application Binary Interfaces(ABI)
,并且將他們歸類到不同的目錄中。這樣做的好處是,可以充分發揮特定的 CPU 架構。你可以使用 ANDROID_ABI
路徑變量,將多個 ABI 版本的庫添加到你的 CMake 構建腳本中。這個變量使用了一些 NDK 默認支持的 ABI,以及一些需要手動配置到 Gradle 的 ABI,比如:

add_library(...)
set_target_properties( # Specifies the target library.
                       imported-lib

                       # Specifies the parameter you want to define.
                       PROPERTIES IMPORTED_LOCATION

                       # Provides the path to the library you want to import.
                       imported-lib/src/${ANDROID_ABI}/libimported-lib.so )

為了讓 CMake 在編譯時期能找到你的頭文件,你需要使用 include_directories() 命令,并且將你的頭文件地址傳進去:

nclude_directories( imported-lib/include/ )

在 CMake 構建腳本中使用 target_link_libraries() 命令,將預構建庫與你本地庫相關聯:

target_link_libraries( native-lib imported-lib app-glue ${log-lib} )

當你構建你的 APP 的時候,Gradle 會自動將導入的庫打包到你的 APK 中。你可以使用 APK Analyzer 來檢查。

1.5、關聯本地庫與 Gradle

為了將本地庫與 Gradle 相關聯,你需要在 CMake 或 ndk-build 構建腳本中提供一個路徑地址。當你構建你的 APP 時,Gradle 會將 CMake 或 ndk-build 作為一個依賴運行,然后將共享庫(.so 文件)打包到你的 APK 中。

1.5.1、使用 Android Studio 圖形化界面

  1. 打開 IDE 左邊的 Project 面板,選擇 Android 視圖。
  2. 右鍵點擊你想鏈接本地庫的 module,比如 app module,然后從菜單中選擇 Link C++ Project with Gradle。你應該能看見一個和下圖很像的對話框。
  3. 在下拉菜單中,選擇 CMake 或者 ndk-build。
    a. 如果你選擇 CMake,需要在 Project Path 中指定 CMakeLists.txt 腳本文件的路徑。
    b. 如果你選擇 ndk-build,你需要在 Project Path 中指定 Android.mk 腳本文件的路徑。

1.6、手動配置 Gradle

如果要手動將 Gradle 與你的本地庫相關聯,你需要在 module 層級的 build.gradle 文件中添加 externalNativeBuild {} 代碼塊,并且在該代碼塊中配置 cmake {} 或 ndkBuild {}:

android {
  ...
  defaultConfig {...}
  buildTypes {...}

  // Encapsulates your external native build configurations.
  externalNativeBuild {

    // Encapsulates your CMake build configurations.
    cmake {

      // Provides a relative path to your CMake build script.
      path "CMakeLists.txt"
    }
  }
}

1.6.1、可選配置

你可以在你的 module 層級的 build.gradle 文件中的 defaultConfig {} 代碼塊中,添加 externalNativeBuild {} 代碼塊,為 CMake 或 ndk-build 配置一些額外參數。當然,你也可以在你的構建配置中,為其他每一個生產渠道重寫這些屬性。

比如,如果你的 CMake 或者 ndk-build 項目中定義了多個本地庫,你想在某個生產渠道使用這些本地庫中的幾個,你就可以使用 targets 屬性來構建和打包。下面的代碼展示了一些你可能會用到的屬性:

android {
  ...
  defaultConfig {
    ...
    // This block is different from the one you use to link Gradle
    // to your CMake or ndk-build script.
    externalNativeBuild {

      // For ndk-build, instead use ndkBuild {}
      cmake {

        // Passes optional arguments to CMake.
        arguments "-DCMAKE_VERBOSE_MAKEFILE=TRUE"

        // Sets optional flags for the C compiler.
        cFlags "-D_EXAMPLE_C_FLAG1", "-D_EXAMPLE_C_FLAG2"

        // Sets a flag to enable format macro constants for the C++ compiler.
        cppFlags "-D__STDC_FORMAT_MACROS"
      }
    }
  }

  buildTypes {...}

  productFlavors {
    ...
    demo {
      ...
      externalNativeBuild {
        cmake {
          ...
          // Specifies which native libraries to build and package for this
          // product flavor. If you don't configure this property, Gradle
          // builds and packages all shared object libraries that you define
          // in your CMake or ndk-build project.
          targets "native-lib-demo"
        }
      }
    }

    paid {
      ...
      externalNativeBuild {
        cmake {
          ...
          targets "native-lib-paid"
        }
      }
    }
  }

  // You use this block to link Gradle to your CMake or ndk-build script.
  externalNativeBuild {
    cmake {...}
    // or ndkBuild {...}
  }
}

1.6.2、指定 ABI

如果你想 Gradle 構建并打包某個特定的 ABI 。你可以在你的 module 層級的 build.gradle 文件中使用 ndk.abiFilters 標簽來指定他們:

android {
  ...
  defaultConfig {
    ...
    externalNativeBuild {
      cmake {...}
      // or ndkBuild {...}
    }

    ndk {
      // Specifies the ABI configurations of your native
      // libraries Gradle should build and package with your APK.
      abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a'
    }
  }
  buildTypes {...}
  externalNativeBuild {...}
}

大多數情況,你只需要像上面的代碼那樣,在 ndk {} 代碼塊中指定 abiFilters 即可。如果你想控制 Gradle 構建、依賴你希望的東西,你就需要在 defaultConfig.externalNativeBuild.cmake {} 代碼塊或 defaultConfig.externalNativeBuild.ndkBuild {} 代碼塊中,配置其他的 abiFilters 標簽。Gradle 會構建這些 ABI 配置,但是只會將 defaultConfig.ndk {} 代碼塊中指定的東西打包到 APK中。

官方文檔:
https://developer.android.com/studio/projects/add-native-code.html#create-cmake-script

推薦一篇不錯的相關文章:
http://blog.csdn.net/mabeijianxi/article/details/68525164

2、ndk-build

ndk-build這里不做詳細介紹,請自行查看官方文檔:
https://developer.android.com/ndk/guides/ndk-build.html

3、experimental-gradle-plugin

experimental-gradle-plugin這里不做詳細介紹,請自行查看官方文檔:
http://tools.android.com/tech-docs/new-build-system/gradle-experimental
官方網址可能打不開,可查看轉載網址:
http://www.cnblogs.com/tanlon/p/4731283.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容