[时间:2017-09] [状态:Open]

[关键词:Android,Android Studio,gradle,native,c,c++,cmake,原生开发,ndk-build]

0 引言

最近在工作中遇到了升级Android Studio 2.3.3稳定版之后,无法编译jar包的问题。之后寻找AS文档-探索 Android Studio发现。可以通过AS创建和编译jar包,顺便看到支持原生开发,可以直接在AS中调试c/c++代码,这是非常不错的功能。终于可以摆脱纯打日志的开发环境了。

本系列文章也就因此而出现。我希望阅读文本文之后,大家能够基本了解如何使用新版的Android Studio开发原生引用。

这里说明一点,本文是参考谷歌的Android Studio官网来的,也可以认为是一种翻译版。可以访问的话你可以直接查看对应内容。

这是第二篇:现有AS项目中添加原生库。

1 环境准备

请参考本系列的第一篇:原生库示例创建及验证中的要求,按照和更新Android Studio。本文需要事先创建一个AS工程,可以不支持原生库。

比如我使用默认的blank Activity创建了一个名为ExistingApplication的项目。

2 主要步骤

如果需要在现有项目中添加原生代码,需要参考下面步骤:(下列步骤推荐用于添加全新的库)

  1. 创建native源文件,并添加到工程中;

    如果你仅仅是导入预构建的原生库或者源码可以跳过此步骤。
  2. 创建一个CMake构建脚本,用于构建你的源代码并创建库。如果是导入或者链接预构建的库也需要该脚本。

    如果你的原生库已经使用CMakeLists.txt构建脚本,或者使用ndk-build构建并包含Android.mk构建脚本,可以跳过此步骤。
  3. 将Gradle与原生库链接起来,只提供下CMake或ndk-build脚本文件所在路径即可。

    Gradle会自动使用构建脚本导入源代码,并将其以SO文件的形式打包到APK中。

通过上面三个步骤,你就可以在AS中使用JNI框架调试和验证原生代码了。

下面分开介绍下各个步骤。

步骤一:创建native源文件,并添加到工程中

在app模块中创建cpp目录用于保存新的原生代码,步骤如下:

  1. 切换到Project Files视图,并找到模块>src,在main目录上右键点击,选择新建>目录。
  2. 输入一个目录名字(比如输出cpp),并点击确定。
  3. 右键点击你刚创建的目录,并选择新建 > C/C++ 源文件。
  4. 输入要创建的源文件的名字,比如native_lib。
  5. 从下拉列表框中,选择源文件的扩展名,比如.cpp。
  6. 如果需要创建头文件,勾选对应的复选框。点击确定即可。

步骤二:创建CMake构建脚本

CMake构建脚本是一个名为CMakeLists.txt的纯文本文件。本部分仅包含在CMake构建本地库时常用的基础命令,更详细的介绍建议参考CMake官方文档

请注意:如果使用ndk-build,无需提供CMake构建脚本。你可以通过提供你的Android.mk文件所在的目录将其与Gradle链接起来。

以下步骤是如何创建CMake构建脚本:

  1. 打开IDE左侧的工程面板,并选择Project视图。
  2. 右键点击模块的根目录,并选择新建 > 文件。
  3. 输入CMakeLists.txt作为文件名,并点击确定。

创建之后,你就可以在构建脚本中添加CMake命令了。为了使用CMake构建原生库,需要在构建脚本中添加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( # 指定库的名称
native-lib # 设置库类型为共享库
SHARED # 提供源码所在的路径
src/main/cpp/native-lib.cpp )

在构建脚本中使用add_library()添加源文件或库时,AS会在Project视图中自动显示对应的头文件。然而,为了让CMake可以在编译时定位头文件的目录,需要在CMake构建脚本中添加include_directories()命令,并指定头文件所在目录。

add_library(...)

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

CMake用于命名库文件的格式如下:liblibrary-name.so

例如,如果你在构建脚本中指定共享库的名字为"native-lib",CMkae将创建一个名为libnative-lib.so的文件。但在Java代码载入库时,需要使用你在CMake构建脚本中指定的名字:

static {
System.loadLibrary("native-lib");
}

添加NDK APIs

Android NDK中提供了一系列有用的原生API及库。你可以在CMakeLists.txt脚本文件中使用这些API,只需要包含NDK库即可。

预构建的NDK库已经在安卓平台上存在,你不需要将其构建或打包到你的APK中。由于NDK库是CMake搜索路径的一部分,你也无须指定你的NDK安装目录,你只需要提供在你的原生库中所依赖的ndk名称即可。

在你的CMake构建脚本中添加find_library()命令,用于定位NDK库所在位置,并将其存储在变量中。你可以构建脚本的其他部分使用该变量来引用NDK库。例如:下面示例代码中定位了安卓相关的log支持库,并将其路径保存在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 )

为了让你的原生库可以调用log库中的函数,你需要使用target_link_libraries()来链接该库。

find_library(...)

# Links your native library against one or more other native libraries.
target_link_libraries( # Specifies the target library.
native-lib # Links the log library to the target library.
${log-lib} )

NDK中包含一些以源码形式存在的库,如果需要的话,你需要将其编译为原生库,通过在CMake构建脚本中添加相应的add_library()命令即可。其中你可以使用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} )

添加其他预构建的库

添加预构建库和使用CMake构建一个原生库类似。不过你需要使用IMPORTED标志来告知CMake:你仅需要将该库导入到你的工程中:

add_library( imported-lib
SHARED
IMPORTED )

你需要指定库所在的路径,使用set_target_properties()命令:

一些库针对不同的CPU架构或ABI(应用程序二进制接口)提供了不同的版本,并将其保存到单独目录下。这种方法可以帮助库仅加载需要版本的库。如果需要在CMake构建脚本中添加多ABI版本的库,不需要为每个版本的库编写对应的命令,只需要使用ANDROID_ABI环境变量即可。这个变量中包含了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()命令实现,这样就可包含你自己的头文件了:

include_directories( imported-lib/include/ )

为了将预构建的库连接到你自己的原生库中,需要在CMake构建脚本中将其添加到target_link_libraries()命令中:

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

为了将预构建的库打包到你的APK中,你需要手工配置Gradle,在sourceSets块中包含你的.so文件所在路径。在构建APK之后,你可以使用APK Analyzer验证Gradle究竟打包了那个库。

包含其他CMake工程

如果你想构建多个CMake工程并将其输出导入到你的Android工程中,你可以将CMakeLists.txt作为顶级CMake构建脚本(同时在Gradle中指定该文件),然后将需添加的CMake工程作为该脚本的依赖项。下面的顶级CMake构建脚本使用add_subdirectory()命令来指定另一个CMakeLists.txt文件作为构建依赖项,然后将其输出链接到当前工程中,这样工作机制和预构建库类似。

# Sets lib_src_DIR to the path of the target CMake project.
set( lib_src_DIR ../gmath ) # Sets lib_build_DIR to the path of the desired output directory.
set( lib_build_DIR ../gmath/outputs )
file(MAKE_DIRECTORY ${lib_build_DIR}) # Adds the CMakeLists.txt file located in the specified directory
# as a build dependency.
add_subdirectory( # Specifies the directory of the CMakeLists.txt file.
${lib_src_DIR} # Specifies the directory for the build outputs.
${lib_build_DIR} ) # Adds the output of the additional CMake build as a prebuilt static
# library and names it lib_gmath.
add_library( lib_gmath STATIC IMPORTED )
set_target_properties( lib_gmath PROPERTIES IMPORTED_LOCATION
${lib_build_DIR}/${ANDROID_ABI}/lib_gmath.a )
include_directories( ${lib_src_DIR}/include ) # Links the top-level CMake build output against lib_gmath.
target_link_libraries( native-lib ... lib_gmath )

步骤三:Gradle链接原生库

为了在Gradle中了链接你的原生库,你需要提供你的CMake或nkd-build脚本文件的路径。配置好之后,在实际构建APP时,Gradle会将CMake/ndk-build作为依赖项,并将共享库打包到APK中。Gradle也会参考该构建脚本,将源文件加载到Android Studio工程,以便你可以直接访问。如果你的native源代码没有对应的CMake构建脚本,你需要参考上一个步骤创建一个。

Android工程总的每个module只能链接一个CMake/ndk-build脚本文件。因此,假如你想同时构建多个CMake工程并打包其输出,你可用一个CMakeLists.txt作为顶层的CMake构建脚本(并在Gradle中指定该文件路径),然后将其他CMake工程作为该构建脚本的依赖项。同样的,如果你使用ndk-build,你可以在顶层的Android.mk脚本文件中包含其他Makefile。

当你在Gradle中配置好native项目之后,Android Studio将会更新Project面板来显示你的源代码文件和原生库(cpp),在External Build Files中显示外部构建脚本。

使用Android Studio UI配置

你可以使用Android Studio UI来实现将Gradle链接到CMake/ndk-build。

  1. 打开IDE左侧的Project面板,并选择Android视图。
  2. 右键点击需要链接原生库的module,比如app module,然后从菜单中选在Link C++ Project with Gradle。你将看到类似图1的对话框。
  3. 从下拉列表框中选择CMake或ndk-build。
    1. 如果使用CMake,在Project Path右侧可以选择你需要添加的CMakeLists.txt脚本文件。
    2. 如果使用ndk-build, 使用Project Patch右侧的选择对话框选择Android.mk脚本文件。如果Application.mk位于和Android.mk相同的目录,也会被自动加载。

  1. 点击确定即可。完成配置。

手动配置Gradle

手动配置Gradle,需要在你的module层的build.gradle文件中添加externalNativeBuild块,并将其使用cmakendkBuild块配置。

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"
}
} // If you want Gradle to package prebuilt native libraries
// with your APK, modify the default source set configuration
// to include the directory of your prebuilt .so files as follows.
sourceSets {
main {
jniLibs.srcDirs 'imported-lib/src/', 'more-imported-libs/src/'
}
}
}

指定可选配置项

你可以为CMake或ndk-build指定可选参数和标志,通过配置你的module层的build.gradle文件中的defaultConfig块下的externalNativeBuild子块。和defaultConfig块中其它属性类似,你可以在你的产品级构建配置中覆盖这些属性。

例如,如果你的CMake或ndk-build工程定义了多个原生库,你可以使用target属性来为特定产品构建和打包一个这些库的子集。下面代码展示了如何通过配置该属性实现上述需求:

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 the ndkBuild block.
cmake { // Passes optional arguments to CMake.
arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang" // Sets optional flags for the C compiler.
cFlags "-fexceptions", "-frtti" // 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"
}
}
}
} // Use this block to link Gradle to your CMake or ndk-build script.
externalNativeBuild {
cmake {...}
// or ndkBuild {...}
}
}

了解更多相关信息,请参考Configure Build Variants。对于CMake支持的参数属性,请参考CMake Variables

指定ABIs

默认情况下,Gradle会按照NDK支持的ABI将你的原生库编译为独立的.so文件。并将它们全部打包到APK中。如果你希望Gradle仅构成和打包指定ABI版本的原生库,你就需要在module层的build.gradle文件中指定ndk.abiFilters标志,如下所示:

android {
...
defaultConfig {
...
externalNativeBuild {
cmake {...}
// or ndkBuild {...}
} // Similar to other properties in the defaultConfig block,
// you can configure the ndk block for each product flavor
// in your build configuration.
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 {...}
}

如果你希望减小APK的大小,请参考基于ABI的多APK打包配置

3 小结及后续

本文作为Android创建的第二篇,整体比较简单,内容主要是翻译部分,整理并介绍了源代码和gradle、CMake构建脚本。接下来我们将关注如何使用AS构建JAR包。

Android Studio添加原生库并自动构建的更多相关文章

  1. 在Xcode5和Android Studio添加工程间的依赖

    正在编辑中,尚未完成 先看看ios的target是什么,请先参看http://www.cocoachina.com/bbs/read.php?tid-10884.html做个大概了解 这里有一篇文章, ...

  2. 手把手图文并茂教你用Android Studio编译FFmpeg库并移植

    转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52661331 之前曾写过一篇&l ...

  3. android studio 导入第三方库的记录

    android studio 导入第三方库的记录.jar包 和 库 一.jar包 1.jar包的话很简单,首先换成project模式,将你要用的jar包复制到lib下面.如图 2.然后右键选择Add ...

  4. Android Studio导入第三方库的三种方法

    叨叨在前 今天在项目中使用一个图片选择器的第三方框架——GalleryFinal,想要导入源码,以便于修改,于是上完查找了一下方法,想到之前用到过其他导入第三方库的方法,现在做个小总结,以防忘记. A ...

  5. Android Studio(六):Android Studio添加注释模板

    Android Studio相关博客: Android Studio(一):介绍.安装.配置 Android Studio(二):快捷键设置.插件安装 Android Studio(三):设置Andr ...

  6. Eclipse / android studio 添加第三方jar包 步骤

    eclipse 将第三方包放到libs文件夹后并没有引用. 基本步骤分为3步,具体介绍如下: 打开自己的Eclipse,在自己的Android工程上名上右键->Build Path ->C ...

  7. Android Studio 添加Assets目录

    Android Studio 添加Assets目录: 法一: Since Android Studio uses the new Gradle-based build system, you shou ...

  8. 【Android Studio安装部署系列】十五、Android studio添加Assets目录

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 概述 Android Studio新建项目时是没有assets目录,需要自己手动创建. app右键——New——Folder——Asset ...

  9. 在Android Studio添加本地aar包引用

    1.如何在Android Studio添加本地aar包引用 https://jingyan.baidu.com/article/2a13832890d08f074a134ff0.html 2.完成上述 ...

随机推荐

  1. url两次编码

    encodeURI函数采用UTF-8对URL进行编码,所以如果服务器在进行解码时使用的是其他的编码方式就会出现乱码,默认的服务器配置的解码字符集都不是UTF-8,所以大部分情况下地址栏提交中文查询参数 ...

  2. SwipeableFlatList 实现类似于QQ列表滑动

    import React, { Component } from "react"; import { FlatList, RefreshControl, StyleSheet, T ...

  3. [P1441]砝码称重 (搜索+DP)

    对于我这种蒟蒻,是很不错的一题了. dfs搜索当前状态 满足时DP 比较坑的地方就是起始的地方 我一开始从1开始,搜索写的是从0开始. 后来就统一用0开始的了. #include<bits/st ...

  4. svn window下过滤文件(如配置文件等)

    第一种: 在资源管理器中,右键一个未加入版本控制文件或目录,并从弹出菜单选择TortoiseSVN →Add to Ignore List,会出现一个子菜单,允许你仅选择该文件或者所有具有相同后缀的文 ...

  5. websocket是什么

    websocket是什么? 这是知乎上面一个作者写的一篇风趣幽默又容易理解关于 websocket 的文章. 提供一下连接地址:https://www.zhihu.com/question/20215 ...

  6. yii2 basic版 MVC 部分

    一.model 1.什么是模型 模型是 MVC 模式中的一部分,是表现业务数据.规则和逻辑的对象 可通过继承 yii\base\Model 或它的子类(activeRecord)来定义模型类,基类 y ...

  7. Vue(十)生命周期

    Vue生命周期 vue实例从创建到销毁的过程,称为生命周期,共有八个阶段 <script> window.onload=function(){ let vm = new Vue({ el: ...

  8. leetcode笔记--SUM问题

    引用自 http://blog.csdn.net/wangxiaojun911/article/details/18922337,此处仅作为自己参考 1.Two SUM Given an array ...

  9. pygame 笔记-3 角色动画及背景的使用

    上二节,已经知道如何控制基本的运动了,但是只有一个很单调的方块,不太美观,本节学习如何加载背景图,以及角色的动画. 素材准备:(原自github) 角色动画的原理:动画都是一帧帧渲染的,比如向左走的动 ...

  10. nat 共享上网软件

    http://www.nat32.com/ SinforNat WinGate