• android studio 使用CMAKE


    前言

    之前,每次需要边写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文件时需要对应修改。

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

    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 {}

    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"
        }
      }
    }

    可选配置

    你可以在你的 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 {...}
      }
    }

    指定 ABI

    一般情况下,Gradle 会将你的本地库构建成 .so 文件,然后将其打包到你的 APK 中。如果你想 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 中。

  • 相关阅读:
    hiho一下 第115周:网络流一•Ford-Fulkerson算法 (Edmond-Karp,Dinic,SAP)
    hiho一下 第二周&第四周:从Trie树到Trie图
    2016 acm香港网络赛 C题. Classrooms(贪心)
    2016 acm香港网络赛 F题. Crazy Driver(水题)
    2016 acm香港网络赛 B题. Boxes
    系统吞吐量(TPS)、用户并发量、性能测试概念和公式(转发)
    使Eclipse下支持编写HTML/JS/CSS/JSP页面的自动提示。
    Tomcat 系统架构与设计模式,第 2 部分: 设计模式分析
    Tomcat 系统架构与设计模式,第 1 部分: 工作原理
    Tomcat源码分析
  • 原文地址:https://www.cnblogs.com/welhzh/p/7461227.html
Copyright © 2020-2023  润新知