前言

之前,每次需要边写C++代码的时候,我的内心都是拒绝的。 
1. 它没有代码提示!!!这意味着我们必须自己手动敲出所有的代码,对于一个新手来说,要一个字母都不错且大小写也要正确,甚至要记得住所有的jni函数等,真是太折磨人了…平时写Java代码的时候都是写几个字母会出来一大堆提示然后选择的,这样还有一个好处就是很多时候我们不知道有那些函数,但是我们可以通过obj.,然后就可以看到它有哪些方法函数了。 
2. 很多地方会显示为红色,就像是错误提示的那种,当然,如果没错的话还是能编译运行的,但是如果像编写java代码一样,写错的地方才给我红色显示不是更好吗? 
这两个问题可折磨死我这个强迫症了…在网上百度了很久如何能够使用Android Studio编写C++代码时有代码提示自动补全功能,但是一直没有找到有效的(不使用CMake的情况下,若有人知道,麻烦告知)。好在一次偶然的机会,在网上看到
Android Studio 2.2 中愉快地使用 C/C++
这篇文章,看到上面说Android
Studio2.2版本完善了对C/C++的支持,还提及到CMAKE,赶紧把Android
Studio从2.0版本升级到了2.2版本,按照介绍新建了一个NDK工程,发现里面居然有我梦寐以求的C++代码自动补全提示!!!喜大普奔!!! 
赶紧了解学习下Android Studio2.2版本提供的CMAKE方式。打算以后都使用这种方式了,别的不说,就冲可以有C++代码自动补全提示这个功能我就爱上它了。

工具安装

Android Studio升级到2.2之后,我们可以先配置好NDK开发的一些所需工具,如图,在SDK Tools中勾选安装CMake、LLDB、NDK。

CMake: 外部构建工具。如果你准备只使用 ndk-build 的话,可以不使用它。  
LLDB: Android Studio上面调试本地代码的工具。

Android Studio自带DEMO了解CMAKE

Android Studio升级到2.2版本之后,在创建新的project时,界面上多了一个Include C++ Support的选项。勾选它之后将会创建一个默认的C++与JAVA混编的Demo程序。就让我们先来看看这个官方标准Demo吧。

开始之前最好先下载好NDK,见NDK开发 从入门到放弃(一:基本流程入门了解),即在Project Structure界面Android NDK location处下载或选择正确的路径。或者使用上方提供的工具安装方法来进行下载。否则,创建的新project也会报错,需要配置好后clean。

File -> New -> New Project,在如下界面中勾选Include C++ Support,然后一路 Next,直到 Finish 为止即可。

项目打开后我们查看目录结构,与常规项目不同的是多了.externalNativeBuild文件夹、cpp文件夹、CMakeLists.txt文件,如下图:

这三个东西都是NDK部分: 
1. .externalNativeBuild文件夹:cmake编译好的文件, 显示支持的各种硬件等信息。系统生成。 
2. cpp文件夹:存放C/C++代码文件,native-lib.cpp文件是该Demo中自带的,可更改。需要自己编写。 
3. CMakeLists.txt文件:CMake脚本配置的文件。需要自己配置编写。

Gradle中也有两处不同:

java代码:

  1. public class MainActivity extends AppCompatActivity {
  2.  
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7.  
  8. // Example of a call to a native method
  9. TextView tv = (TextView) findViewById(R.id.sample_text);
  10. tv.setText(stringFromJNI());
  11. }
  12.  
  13. /**
  14. * A native method that is implemented by the 'native-lib' native library,
  15. * which is packaged with this application.
  16. */
  17. public native String stringFromJNI();
  18.  
  19. // Used to load the 'native-lib' library on application startup.
  20. static {
  21. System.loadLibrary("native-lib");
  22. }
  23. }

从native-lib.cpp的代码中我们能看到它使用的是静态注册的方式,动态注册的方式代码同传统JNI。

  1. #include <jni.h>
  2. #include <string>
  3.  
  4. extern "C"
  5. jstring
  6. Java_com_example_person_myapplication_MainActivity_stringFromJNI(
  7. JNIEnv* env,
  8. jobject /* this */) {
  9. std::string hello = "Hello from C++";
  10. return env->NewStringUTF(hello.c_str());
  11. }

CMakeLists.txt文件中我们需要注意的是下面这三个地方。两个library的名字(需一致)以及一个cpp文件的路径,彼此需要对应一致,当我们自己定义library以及自己创建cpp文件时需要对应修改。

运行代码,就能看到效果,调用了C++方法在界面上显示了Hello from C++字符串。这就是CMake方式进行NDK开发的Demo。 
还记得传统JNI方式中吗?我们使用了ndk
-build来编译C/C++文件为so文件,对于使用过第三方库的开发者来说,对so肯定不陌生。我们只能使用别人给的so文件,而无法看到C/C++源码,更别说去修改了。但是在这里我们好像一直没看到so文件的影子,那么,我们安装运行的apk中,有对应的so文件吗?如果想验证一下apk是否有so文件,我们可以使用
APK Analyzer:

    1. 选择 Build > Analyze APK。
    2. 选择 apk,并点击 OK。
    3. 如下图,在 APK Analyzer 窗口中,选择 lib/x86/,可以看见 libnative-lib.so 。

Tips:Instant Run 并不兼容使用了 native code 的项目。Android Studio 会自动禁止 Instant Run 功能。

但是我个人目前更关注的问题是C++代码自动补全提示的功能,当在cpp文件中写代码时,里面不再是一大片的红色,输入若干个字母的时候,也能给出提示了,简直叼渣天,至于原因…我就不清楚了,为什么在这里就有这个强大的功能了呢?哪位大神能解释下…

CMake编译so文件

CMake和传统 JNI在目录结构和配置文件上的区别

现在我们有了CMake和传统JNI两种开发NDK的方法,它们在目录结构和Gradle上有所区别,下面我们将分别介绍目录区别和Gradle配置的区别。 
一、目录结构 
传统JNI

CMake

这两种方式在目录上的区别就是两点: 
1. 以前的jni目录改成cpp,名字更换了,下面还是存放C/C++文件。  
2. 之前对C/C++文件的编译配置Android.mk、Application.mk文件放在jni目录下,现在改成CMakeLists.txt文件。(事实上这些文件的位置是可任意存放的,只需要配置好就行。但最好还是按照默认习惯放置。)

二、Gradle 
传统JNI

CMake

事实上,我们在使用传统JNI方式的时候,上面的两处地方我们都省略了,也不会造成什么异常(若Android.mk存放位置在其他地方则需要配置)。CMake方式中第一处也可省略,但是第二处不能省略(同样,位置路径要写对),因为没有它将无法Build生成.externalNativeBuild文件夹。

另外,传统JNI开发还需要在项目根目录下的gradle.properties文件中配置

  1. android.useDeprecatedNdk=true
  • 1
  • 1

否则Build项目的时候会报错。

CMake的优势

  1. 可以直接的在C/C++代码中加入断点,进行调试
  2. java引用的C/C++中的方法,可以直接ctrl+左键进入
  3. 对于include的头文件或者库,也可以直接进入
  4. 不需要配置命令行操作,手动的生成头文件,不需要配置android.useDeprecatedNdk=true属性

普通Android项目转NDK开发项目

我们之前说了,在创建新project时若勾选了Include C++ Support选项(需Android Studio版本不低于2.2),则该项目就已经是一个NDK开发项目了。那么,若项目已经存在,该如何再转为NDK开发项目呢?

Tips: 在配置好NDK的前提下。且各种地方的配置均不再赘述。如Android.mk中如何配置、CMakeLists.txt文件中的配置、各个地方的统一等。

传统JNI方式,在NDK开发 从入门到放弃(一:基本流程入门了解)中我们了解过:

  1. 新建jni目录,写好C/C++代码。静态注册JNI时我们使用了javah -jni对JAVA类进行操作自动生成了jni目录以及对应的头文件(事实上,当我们有一定经验后可以自己写,而不再需要使用该辅助命令来保证不写错,另外动态注册也是一个很值得提倡的方式),然后根据头文件写了C/C++代码。但在动态注册JNI时我们可以自己先创建好jni目录且写好C/C++代码。
  2. 在jni目录下创建且配置好Android.mk和Application.mk两个文件。
  3. build.gradle文件中根据情况进行配置,可不进行配置使用默认值。
  4. 通过ndk-build操作,我们能得到对应的so文件,放置在相应位置,java代码中即可调用C/C++代码,运行程序。

回顾前面提到的CMake方式的NDK开发,我们得到如下步骤:

  1. 新建cpp目录,写好C/C++代码。
  2. 创建且配置CMakeLists.txt文件。
  3. build.gradle文件中根据情况进行配置,CMakeLists.txt文件的路径必须配置。
  4. java代码中即可调用C/C++代码,运行程序。
  5. project的build.gradle文件中,gradle版本不能低于2.2,否则会报错。

关联本地库与 Gradle

为了将本地库与 Gradle 相关联,你需要在 CMake 或 ndk-build 构建脚本中提供一个路径地址。当你构建你的 APP 时,Gradle 会将 CMake 或 ndk-build 作为一个依赖运行,然后将共享库(.so 文件)打包到你的 APK 中。Gradle 同样使用构建脚本来识别哪些文件需要导入到 Android Studio 项目,你可以从 Project 窗口面板中看到相应的文件。如果你还没有一个为 native sources 准备的构建脚本,你需要先创建一个。

使用 Android Studio 图形化界面

你可以使用 Android Studio 的图形化界面来将 Gradle 与外部 CMake 或者 ndk-build 项目关联起来。

    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. 点击 OK

手动配置 Gradle

如果要手动将 Gradle 与你的本地库相关联,你需要在 module 层级的 build.gradle 文件中添加 externalNativeBuild {} 代码块,并且在该代码块中配置 cmake {}ndkBuild {}

  1. android {
  2. ...
  3. defaultConfig {...}
  4. buildTypes {...}
  5.  
  6. // Encapsulates your external native build configurations.
  7. externalNativeBuild {
  8.  
  9. // Encapsulates your CMake build configurations.
  10. cmake {
  11.  
  12. // Provides a relative path to your CMake build script.
  13. path "CMakeLists.txt"
  14. }
  15. }
  16. }

可选配置

你可以在你的 module 层级的 build.gradle 文件中的 defaultConfig {} 代码块中,添加 externalNativeBuild {} 代码块,为 CMake 或 ndk-build 配置一些额外参数。当然,你也可以在你的构建配置中的其他每一个生产渠道重写这些属性。

比如,如果你的 CMake 或者 ndk-build 项目中定义了多个本地库,你想在某个生产渠道使用这些本地库中的几个,你就可以使用 targets 属性来构建和打包。下面的代码展示了一些你可能会用到的属性:

  1. android {
  2. ...
  3. defaultConfig {
  4. ...
  5. // This block is different from the one you use to link Gradle
  6. // to your CMake or ndk-build script.
  7. externalNativeBuild {
  8.  
  9. // For ndk-build, instead use ndkBuild {}
  10. cmake {
  11.  
  12. // Passes optional arguments to CMake.
  13. arguments "-DCMAKE_VERBOSE_MAKEFILE=TRUE"
  14.  
  15. // Sets optional flags for the C compiler.
  16. cFlags "-D_EXAMPLE_C_FLAG1", "-D_EXAMPLE_C_FLAG2"
  17.  
  18. // Sets a flag to enable format macro constants for the C++ compiler.
  19. cppFlags "-D__STDC_FORMAT_MACROS"
  20. }
  21. }
  22. }
  23.  
  24. buildTypes {...}
  25.  
  26. productFlavors {
  27. ...
  28. demo {
  29. ...
  30. externalNativeBuild {
  31. cmake {
  32. ...
  33. // Specifies which native libraries to build and package for this
  34. // product flavor. If you don't configure this property, Gradle
  35. // builds and packages all shared object libraries that you define
  36. // in your CMake or ndk-build project.
  37. targets "native-lib-demo"
  38. }
  39. }
  40. }
  41.  
  42. paid {
  43. ...
  44. externalNativeBuild {
  45. cmake {
  46. ...
  47. targets "native-lib-paid"
  48. }
  49. }
  50. }
  51. }
  52.  
  53. // You use this block to link Gradle to your CMake or ndk-build script.
  54. externalNativeBuild {
  55. cmake {...}
  56. // or ndkBuild {...}
  57. }
  58. }

指定 ABI

一般情况下,Gradle 会将你的本地库构建成 .so 文件,然后将其打包到你的 APK 中。如果你想 Gradle 构建并打包某个特定的 ABI 。你可以在你的 module 层级的 build.gradle 文件中使用 ndk.abiFilters 标签来指定他们:

  1. android {
  2. ...
  3. defaultConfig {
  4. ...
  5. externalNativeBuild {
  6. cmake {...}
  7. // or ndkBuild {...}
  8. }
  9.  
  10. ndk {
  11. // Specifies the ABI configurations of your native
  12. // libraries Gradle should build and package with your APK.
  13. abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
  14. 'arm64-v8a'
  15. }
  16. }
  17. buildTypes {...}
  18. externalNativeBuild {...}
  19. }

大多数情况,你只需要像上面的代码那样,在 ndk {} 代码块中指定 abiFilters 即可。如果你想控制 Gradle 构建、依赖你希望的东西,你就需要在 defaultConfig.externalNativeBuild.cmake {} 代码块或 defaultConfig.externalNativeBuild.ndkBuild {} 代码块中,配置其他的 abiFilters 标签。Gradle 会构建这些 ABI 配置,但是只会将 defaultConfig.ndk {} 代码块中指定的东西打包到 APk 中。

android studio 使用CMAKE的更多相关文章

  1. android studio使用CMake和NDK,实现应用自身被卸载时打开某一网址

    实现应用自身被卸载时打开某一网址的c代码 MyActivity: public class MyActivity extends Activity { /** * Called when the ac ...

  2. 【Android Studio安装部署系列】二十五、Android studio使用NDK生成so文件和arr文件

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 概述 Android Studio使用ndk的简单步骤. NDK环境搭建 下载NDK 下载链接:https://developer.and ...

  3. OpenCV - Android Studio 2.2 中利用CAMKE进行OpenCV的NDK开发

    我在http://www.cnblogs.com/fx-blog/p/8206737.html一文中提到了如何在Android Studio中Java层导入OpenCV(包含opencv_contri ...

  4. Android开发学习之路--Android Studio cmake编译ffmpeg

      最新的android studio2.2引入了cmake可以很好地实现ndk的编写.这里使用最新的方式,对于以前的android下的ndk编译什么的可以参考之前的文章:Android开发学习之路– ...

  5. 通过android studio的gradle强制cmake输出命令详情

    https://stackoverflow.com/questions/43439549/force-cmake-in-verbose-mode-via-gradle-and-the-android- ...

  6. 将应用代码由eclipse导入Android studio的方法NDK-Build和Cmake两种方法(以android_serialport_api为例)

    网上翻了几百篇博客,看了半天,要不就是写的乱七八糟看不懂,要不就是隐藏了一些细节,要不就是实现不了,最后还是在Android官网上看明白了,而且说得有条有理,以后遇到不懂的一定要先翻官网. 参考资料: ...

  7. android ndk-build 编译静态库libxx.a 以及Android studio openssl 静态库配置(cmake)

    android ndk-build 编译静态库libxx.a 需求场景: 目前有安卓编码好的现在的openssl的两个.a,我们需要调用openssl的函数,并把功能再封装成.a; 这样使用时,在an ...

  8. Android Studio 2.2以上支持了Cmake的配置JNI的相关参数

    Android Studio 2.2以上支持了Cmake的配置JNI的相关参数,简化了通过Android.mk配置.并很好的继承了C++的编辑方式.以下是对应的引入第三方so和第三方.cpp文件的路径 ...

  9. Android Studio中通过CMake使用NDK并编译自定义库和添加预编译库

    Note:这篇文章是基于Android Studio 3.01版本的,NDK是R16. step1:创建一个包含C++的项目 其他默认就可以了. C++ Standard 指定编译库的环境,其中Too ...

随机推荐

  1. FairyGUI学习

    官网:http://www.fairygui.com/ 教程:http://www.taikr.com/course/446/tasks 博客:http://gad.qq.com/article/de ...

  2. Unity3D Android手机屏幕分辨率问题

    Android手机屏幕分辨率五花八门,导致开发时不好把握,还好各个引擎对这个屏幕分辨率问题都有较好的处理方式:unity3D 也为我们提供了一个不错的解决方案. 在Unity3D 进行 android ...

  3. 怎么输入MathType不等号

    MathType是一款比较常用的数学公式编辑器,我们在使用这款软件的时候常常需要输入各种符号.有些新用户对这款软件不是很熟悉,对于一些符号不知道怎么输入,下面我们来给大家介绍介绍MathType不等号 ...

  4. Maven------pom.xml自动加载各种类库代码

    转载: http://lavasoft.blog.51cto.com/62575/1388866/ 一般要加<type>jar</type> <dependency> ...

  5. python常用内置模块,执行系统命令的模块

    Subprocess模块 python3.5将使用Subprocess模块跟操作系统进行交互,比如系统命令,他将替换 os.system os.spawn* subprocess.run()方法封装的 ...

  6. Python zmail 模块

    zmail 是 python3 用来收发邮件的一个模块,用法参考: https://mp.weixin.qq.com/s?__biz=MzAxMjUyNDQ5OA==&mid=26535559 ...

  7. eclipse启动错误

    1.错误日志 !SESSION 2013-12-09 12:24:33.826 -----------------------------------------------eclipse.build ...

  8. JS AJAX传递List数组到后台(对象)

    今天在写代码的时候,碰到的问题,百度了一下,发现原来AJAX传递List数据是可以的,之前还一直用JSON序列化(new Array()数组设置)进行传值的. var _list = {}; //等价 ...

  9. 【BZOJ4545】DQS的trie 后缀自动机+LCT

    [BZOJ4545]DQS的trie Description DQS的自家阳台上种着一棵颗粒饱满.颜色纯正的trie. DQS的trie非常的奇特,它初始有n0个节点,n0-1条边,每条边上有一个字符 ...

  10. ios UITableView默认选中第一行

    NSIndexPath *ip = [NSIndexPath indexPathForRow:0inSection:0]; [titleTableViewselectRowAtIndexPath:ip ...