NDK开发 从入门到放弃 七 Android Studio 2 2 CMAKE 高效NDK开发

首先给大家推荐一下我老师大神的人工智能教学网站。教学不仅零基础,通俗易懂,而且非常风趣幽默,还时不时有内涵黄段子!点这里可以跳转到网站

前言

之前,每次需要边写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代码:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    // Example of a call to a native method
    TextView tv = (TextView) findViewById(R.id.sample_text);
    tv.setText(stringFromJNI());
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }
}

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

#include <jni.h>
#include <string>

extern "C"
jstring
Java_com_example_person_myapplication_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
这里写图片描述
这里写图片描述

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

其中,add_library方法和target_link_libraries方法一一对应,当有多个库,多个C/C++文件时,可写多组add_library方法和target_link_libraries方法。如下图(删除了注释部分)。

运行代码,就能看到效果,调用了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文件

在Android Studio的Terminal中输入cmake,会提示:

'cmake' 不是内部或外部命令,也不是可运行的程序或批处理文件。     
这里写图片描述

需要在下载配置好CMake的环境变量,若没这个问题可跳过这个步骤。在如下网址中下载好对应的文件:
https://cmake.org/download/

下载zip包后,解压缩。然后将cmake.exe所在的路径,添加到path环境变量中。cmake就成为有效命令了。


在CMakeLists.txt文件上级目录下执行cmake .命令,报错,暂未解决:

-- Building for: NMake Makefiles
-- The C compiler identification is unknown
-- The CXX compiler identification is unknown
CMake Error in CMakeLists.txt:
  The CMAKE_C_COMPILER:

    cl

  is not a full path and was not found in the PATH.

  To use the NMake generator with Visual C++, cmake must be run from a shell
  that can use the compiler cl from the command line.  This environment is
  unable to invoke the cl compiler.  To fix this problem, run cmake from the
  Visual Studio Command Prompt (vcvarsall.bat).

  Tell CMake where to find the compiler by setting either the environment
  variable "CC" or the CMake cache entry CMAKE_C_COMPILER to the full path to
  the compiler, or to the compiler name if it is in the PATH.


CMake Error in CMakeLists.txt:
  The CMAKE_CXX_COMPILER:

    cl

  is not a full path and was not found in the PATH.

  To use the NMake generator with Visual C++, cmake must be run from a shell
  that can use the compiler cl from the command line.  This environment is
  unable to invoke the cl compiler.  To fix this problem, run cmake from the
  Visual Studio Command Prompt (vcvarsall.bat).

  Tell CMake where to find the compiler by setting either the environment
  variable "CXX" or the CMake cache entry CMAKE_CXX_COMPILER to the full path
  to the compiler, or to the compiler name if it is in the PATH.


-- Configuring incomplete, errors occurred!
See also "E:/AndroidDemo_XY/app/CMakeFiles/CMakeOutput.log".
See also "E:/AndroidDemo_XY/app/CMakeFiles/CMakeError.log".

待完成


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文件中配置

android.useDeprecatedNdk=true

否则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,否则会报错。

Error: Could not find method externalNativeBuild() for arguments

[build_e8k5d0w74qu1ajxjrrcr9snog$_run_closure1$_closure6@1398219d]

on
project ‘:app’ of type org.gradle.api.Project.。

点这里可以跳转到人工智能网站