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



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值