详解Android studio ndk配置cmake开发native C

Android 2.2 以后的版本对NDK的支持已经非常好了。最近把一个纯C的android项目,从eclipse ADT迁移到Android studio上。本文是参考Add C and C++ Code to Your Project官方文档(需要翻墙),经过各种尝试之后的总结。

Android studio整合NDK开发,有两种模式,一种是ndk build,一种是cmake,如果是新项目官方推荐cmake。原来,ADT的时候只能用ndk build,这次切换IDE并没有选用ndk build,而是尝试了cmake感觉上配置更加简洁方便。

本文探讨一下几点:

1. 迁移现有native C代码使用cmake,如果是新项目同理更加简单。
2. 项目是native activity就是没有java代码的纯native project。
3. 构建编译出多个so文件,并有依赖关系。
4. 使用不依赖IDE目录结构的代码目录。
5. 创建过程中的注意事项。

创建native项目,可以有两个选项。第一个是创建的时候,选择带有C++ Support功能的。

第二个是对已有工程添加c/c++功能。这里,无论是不是新项目,都推荐使用创建一个项目在添加c/c++功能,这样native code就可以独立于项目放在任意目录。创建一个没有native code工程,在根据CMakeLists.txt文件来添加NDK的支持。File -> Link C++ Project with Gradle。

这样,我们的代码就可以独立于IDE的目录结构。只要提供CMakeLists.txt文件即可。一旦我们提供了CMakeLists.txt文件,Android studio就会根据这个文件为我们在工程下面生成一个cpp文件夹用来存放CMakeLists.txt里面配置的native代码文件。

下面我们来快速的介绍一下CMakeLists.txt基本功能的写法,能够应付通常的情况。更多丰富的使用规则需要查看官方文档。CMake documentation

# Sets the minimum version of CMake required to build the native
# library. You should either keep the default value or only pass a
# value of 3.4.0 or lower. 

cmake_minimum_required(VERSION 3.4.1) 

##################################################################### 

# 这个是设置了编译C的参数,这里使用C99并开启三级优化
# 类似的设置还有CMAKE_CPP_FLAGS就是设置编译C++的参数
# 更多的参数就要根据需要看文档了
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -std=c99") 

##################################################################### 

# 这个函数是用来编译库的,主要是so文件和a文件。
add_library( # 括号不在这一行语法错误
  # 库的名字自定义的
  PNG    

  # static 就是a文件,shared 就是so文件
  STATIC   

  # 这里提供的是预编译好的文件,所以用这个imported,
  # 否则需要提供需要编译文件的列表
  IMPORTED
) 

# 设置编译库文件的属性,有很多属性设置,根据需要查看文档
set_target_properties(
  # 设置哪个库的编译属性
  PNG         

  # 上面的PNG库是预编译的,这里的属性表示文件所在的位置
  PROPERTIES IMPORTED_LOCATION  

  # 提供预编译文件的位置。
  # CMAKE_SOURCE_DIR 是内置变量表示当前CMakeLists.txt的位置。
  # 这里需要提供绝对路径所以需要这个变量,
  # 下面会看到所有的设置都是相对于当前文件的。但这个设置需要绝对路径。
  # ANDROID_ABI内置变量,会根据当前编译的平台分配一个文件夹名字,
  # 比如armeabi-v7a, armeabi,x86等等
  ${CMAKE_SOURCE_DIR}/PNG/Prebuilt/Android/${ANDROID_ABI}/libpng.a
) 

##################################################################### 

# 表示编译文件时候,头文件的位置。路径是相对于当前文件的
# 正确设置了这个路径,在IDE中代码头文件也会正确索引。否则会无法定位头文件。
# 这里我们提供了代码的文件的根目录和PNG库的头文件目录
include_directories(
  ../../../
  ../../External/PNG/Include/Android/
) 

# 另外一个用法。编译so文件,自定义名字叫做NativeLib
# 就像NDK Build的配置一样,需要把源文件列表提供,不需要头文件。
# 这些源文件会编译成一个NativeLib.so文件。
# 值得一提的时候,在NDK Build中,我编译一个没有源文件的so文件,
# 以后把其他的a文件整体连接进来。这里不行,必须提供源文件至少一个。
add_library(
  NativeLib SHARED 

  ../../Toolkit/Toolkit.c
  ../../Toolkit/Math/Math.c
  ../../Toolkit/Math/Matrix.c
  ../../Toolkit/Math/TweenEase.c
  ../../Toolkit/Utils/Array.c
  ../../Toolkit/Utils/ArrayList.c
  ../../Toolkit/Utils/ArrayStrMap.c
  ../../Toolkit/Utils/ArrayIntMap.c
  ../../Toolkit/Utils/ArrayQueue.c
  ../../Toolkit/Utils/BufferReader.c
  ../../Toolkit/Utils/Json.c
  ../../Toolkit/Utils/Tween.c
  ../../Toolkit/Utils/TweenTool.c
  ../../Toolkit/Platform/File.c
) 

# 这是编译一个a文件。可见此函数可以使用任意多个,编译出多个库文件。
add_library(
  EntryLink STATIC
  ../../Application/EntryLink.c
) 

# 这是连接一个库文件。在库文件使用了平台,或是预编译库的接口文件,就需要在此连接。
# 才能在运行时正确调用到这些接口函数。
target_link_libraries(
  # 需要连接的库名字,上面定义的任何一个库都行。
  NativeLib 

  # 这里奇怪的参数,是让PNG这个库直接拷贝到NativeLib里面。
  # 因为并不打算把PNG这个库单独载入,平台也不一定有这个库,
  # 于是就整体复制到NativeLib.so里面
  "-Wl,--whole-archive"
  PNG
  "-Wl,--no-whole-archive"   

  # 这个库存在的意义是
  # 比如我在NativeLib用到了一些接口函数,希望留给另外一个库使用。
  # 连接的时候,不提供另外一个库,或是那个库还没编译。就会连接失败找不到函数实现。
  # 所以我们用这个库实现空的函数,用作连接。
  # 并不会放到NativeLib.so里。真正运行的时候,有别的so库文件提供。
  EntryLink   

  # 以下就是Android平台提供的库直接写名字就行了。官方文档有说明哪些。
  android
  EGL
  GLESv2
  log
  z
) 

那么编译出来的库文件在为什么位置呢,如下:

系统生成apk的时候,会自动安装进去。那么,有些情况,能不能自己控制库文件的输出的目录能。当然是可以的,参看NDK官方的例子,hello-libs

add_library(gmath STATIC src/gmath.c)
set_target_properties(gmath
           PROPERTIES
           # 拷贝到下面的指定目录,注意这个属性名,这是拷贝a文件的。
           ARCHIVE_OUTPUT_DIRECTORY
           ${CMAKE_CURRENT_SOURCE_DIR}/lib/${ANDROID_ABI}) 

add_library(gperf SHARED src/gperf.c)
set_target_properties(gperf
           PROPERTIES
           # 拷贝到下面的指定目录,注意这个属性名,这是拷贝so文件的。
           LIBRARY_OUTPUT_DIRECTORY
           ${CMAKE_CURRENT_SOURCE_DIR}/lib/${ANDROID_ABI}) 

接下来的问题就是,如果我有多个不同库功能不同,源码很多不能放在一起编译。希望能够模块化管理,有两个方案。

第一个方案,给工程添加一个依赖模块,用同样的方法link一个CMakeLists.txt这样。如果这样,工程就有两个模块不同的gradle配置,就需要我们用上面的方法把作为库文件产生的so文件编译到指定目录下面,在添加预编译文件的方式进行连接。我开始是用的这个方法,可以工作但感觉并不好,NDK的例子hello-libs也是用的这个方法。后来我发现了一个跟简单的方法。

第二个方案,利用CMake的add_subdirectory函数,可以添加一个子目录,去让CMakeLists.txt再去载入另外一个CMakeLists.txt。这正是我们需要方法。类似于NDK Build里面的嵌套mk文件。

两种方案都会把多个CMakeLists.txt文件导入到Android Studio里面。

# Sets the minimum version of CMake required to build the native
# library. You should either keep the default value or only pass a
# value of 3.4.0 or lower. 

cmake_minimum_required(VERSION 3.4.1) 

set(CMAKE_VERBOSE_MAKEFILE ON) 

##################################################################### 

# 第一个参数表示需要加载的子目录CMakeLists.txt文件目录
# 第二个参数表示编译这个文件内容的中间文件目录
# 都是绝对路径,所以我们使用了内置变量,来跨平台
add_subdirectory(
  ${CMAKE_SOURCE_DIR}/../../../NativeLib/Build/Android/
  ${CMAKE_SOURCE_DIR}/../../../NativeLib/Build/Android/Bin/
) 

##################################################################### 

include_directories(
  ../../../
) 

add_library(
  Development SHARED 

  ../AppInit.c
  ../Tool.c
  ../GameMap.c
  ../Hero.c
  ../Enemy.c
  ../EnemyAI.c
  ../GameActor.c
) 

##################################################################### 

target_link_libraries(
  Development
  NativeLib
)

如上,我们把NativeLib作为库编译,Development依赖这个库。需要注意的是,在子目录的CMakeLists.txt中内置变量CMAKE_SOURCE_DIR是父目录的值,而不是当前文件目录。另外,可以看到我们编译出了两个so文件,链接它们。这样在java中就需要载入两个so文件。其实我是想合并两个so的,但是利用"-Wl,--whole-archive"属性的时候,会发生libc.so里面很多重定义。经过google发现这个可能是NDK的一个bug并没有修复。

当然,也可以只生成一个so文件。就是让NativeLib编译为STATIC的,然后在Development target_link_libraries的时候使用"-Wl,--whole-archive"完全把NativeLib的a文件合并到Development里面就可以了。

最后,就是一个Gradle的配置了。

apply plugin: 'com.android.application' 

android {
  compileSdkVersion 23
  buildToolsVersion '25.0.0'
  defaultConfig {
    applicationId 'com.test.development'
    minSdkVersion 19
    targetSdkVersion 23
    versionCode 1
    versionName '1.0'
    ndk {
      // 这里控制NDK编译哪些类型的ABI so文件,用来适配不同平台
      abiFilters 'armeabi-v7a'
    }
    externalNativeBuild {
      // 使用cmake,还可以使用ndk
      cmake {
        arguments '-DANDROID_TOOLCHAIN=clang',
             '-DANDROID_STL=system'
        cFlags  '-std=c99'
      }
    }
  }
  buildTypes {
    release {
      minifyEnabled false
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
  }
  externalNativeBuild {
    cmake {
      // 定位文件,link的时候自动生成
      path '../../Build/Android/CMakeLists.txt'
    }
  }
} 

dependencies {
  compile fileTree(include: ['*.jar'], dir: 'libs')
} 

cmake的参数配置,arguments可以参看官方文档 Using CMake Variables,更多的gradle cmake配置在这里 Configure Build Types,需要科学上网。当然也可以自定义自己需要的参数,比如fire_base_sdk_dir用在cmake的配置中。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android studio 使用Debugger问题(代码中含有ndk)

    NDK NDK 是 Native Developmentit的缩写,是Google在Android开发中提供的一套用于快速创建native工程的一个工具. 使用这个工具可以很方便的编写和调试JNI的代码. NDK可从官网或AndroidDevTools(个人网站)下载. 1.出现错误: Error running app: Unable to open debugger port (localhost:8601): java.net.ConnectException "Connection re

  • 详解Android studio ndk配置cmake开发native C

    Android 2.2 以后的版本对NDK的支持已经非常好了.最近把一个纯C的android项目,从eclipse ADT迁移到Android studio上.本文是参考Add C and C++ Code to Your Project官方文档(需要翻墙),经过各种尝试之后的总结. Android studio整合NDK开发,有两种模式,一种是ndk build,一种是cmake,如果是新项目官方推荐cmake.原来,ADT的时候只能用ndk build,这次切换IDE并没有选用ndk bui

  • 详解Android Studio中Git的配置及协同开发

    一. Android Stutio配置git setting–>Version Control–>Git–>Path to Git executable中选择git.exe的位置,这个Stutio一般会默认配置好: 配置完路径后点击后面的Test按钮,出现下面提示框则表示配置成功: 二. 将项目分享到github 1. 设置github账号密码 打开Setting–>Version Control–>GitHub,填写完账号密码后,点击Test测试,如果连接成功会弹出如下提示

  • Android+OpenCV4.2.0环境配置详解(Android studio)

    仅是个人记录,希望能对有需要的给予一些小小的帮助 首先我们肯定是要去到OpenCV的官网下载对应的SDK,并解压得到文件夹(opencv-4.2.0-android-sdk) 其次是NDK环境搭建(双击shift,输入sdk,找到sdk manager,将下面红色框框勾选安装) 创建项目,我选用的是(并不是只有这一选择) 导入Module File->New->Import Module 路径选择**\opencv-4.2.0-android-sdk\OpenCV-android-sdk\sd

  • 详解Android studio如何导入jar包方法

    下面我就总结一下Android studio大家在导入jar包时遇到的一些问题和解决方法: 1,首先先说一下怎么在AS 中找到sdk,jdk,ndk的安装路径,可能一部分人一开始找不到,下面贴出方法: Android studio 中更改sdk的路径,如下图,在右边红色方框中更改sdk的路径 还有一种更好的方式可以把sdk,jdk,ndk的路径全部找到,首先File---Other Settings---Default Project Structure...,打开如下图界面,从红方框处即可直接

  • 详解Android Studio正式签名进行调试的实现步骤

    详解Android Studio正式签名进行调试的实现步骤 在Android Studio中,可以使用Gradle进行打包时自动签名.其实Android Studio默认会给调试应用加上Debug签名,但有时候调一些第三方SDK时,需要正式签名才能调起来,所以接下来分享一下使用Gradle自动签名的方法. 一.创建签名文件 打开AS,选择Build->Generate Signed APK,选择要打包的项目,点击Next,再点击Create new...创建签名文件 填写签名文件响应信息,如下所

  • 详解Android Studio实现用户登陆界面demo(xml实现)

    使用Android Studio 编写的第一个demo,使用布局文件-xml实现用户登录界面 注:所建工程均为Android 6.0 所以只要是Android 6.0(包括6.0)以上的真机,模拟机都可以使用 Step1:Android Studio 开发环境的搭建: 1.安装JDK (1.8): 2.安装Android studio (3.3.1) 包含 gradle.sdk manage .avd manage : 3.使用sdk manage 下载安装 sdk: 4.使用avd manag

  • 详解Android studio实现语音转文字功能

    目录 一.在科大讯飞的官网上注册并下载SDK 二.配置安卓项目 三.运行效果展示 一.在科大讯飞的官网上注册并下载SDK 1.首先去讯飞开放平台申请一个账号(https://www.xfyun.cn/),然后点击“控制台”进入新的页面,创建一个应用,找到“语音听写”,下载相应的SDK. 文件解压后内容如下: 二.配置安卓项目 1.在android studio中新建一个空项目,将libs文件夹中的内容复制到安卓项目的libs文件夹下,其中msc.jar要右键添加Add As Library: 2

  • 详解Android studio 3+版本apk安装失败问题

    studio2.3升级到3.1之后将apk发给别人下载到手机上安装,华为提示安装包无效或与操作系统不兼容,魅族提示apk仅为测试版,要求下载正式版安装. 在网上找了一下,发现是studio3.0之后的instant run功能引起的,直接点击绿色箭头按钮烧出来的apk都是不完整的,也就是魅族指的测试版,并且这个apk的路径在app\build\intermediates\instant-run-apk\debug下,而原来的app\build\outputs\apk\debug路径下已经没有ap

  • 详解Android studio 动态fragment的用法

    fragment的使用时Android的基础,它有两种用法,第一个就是静态的fragment.第二个则是动态的fragment. 静态fragment直接在layout创建你想要的fragment的XML的文件,然后在你的Java包里面创建对应fragment的class文件 布局代码如下所示 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http:

  • 详解Android Studio 3.0的新特性与适配

    简介 Android Studio升级到3.0后,有不少的改动和新特性,先贴出官方的迁移说明. 本文会持续收集与总结本人在使用Android Studio 3.0进行开发的过程中所遇到的问题. 版本配置 Gradle版本 Android Studio 3.0需要的Gradle版本至少为4.1. 如果是使用gradle wrapper,则工程根目录/gradle/wrapper/gradle-wrapper.properties中的distributionUrl字段为https\://servic

随机推荐