天天看点

Android NDK 编译工具CMake的使用

Android NDK 编译工具CMake的使用

    • 1. 创建新 Android 项目
    • 2.自定义C++的配置
    • 3.分析项目结构
      • 3.1 so库/native方法
      • 3.2 原生源文件
      • 3.3 构建配置
      • 3.4 脚本配置
      • 3.5 运行流程
    • 参考资料
    • Author
Android Studio 用于构建原生库的默认工具是 CMake。由于很多现有项目都使用构建工具包编译其原生代码,Android Studio 还支持 ndk-build。不过,如果您在创建新的原生库,则应使用 CMake。

创建支持原生代码的项目与创建任何其他 Android Studio 项目类似,不过前者还需要额外几个步骤:

1. 创建新 Android 项目

创建新的 Android 项目,需要选中 Include C++ Support 复选框,即支持C++的。

Android NDK 编译工具CMake的使用

2.自定义C++的配置

依次

Next

后,

Create Android project

>

Target Android Devices

>

Add an Activity to Mobile

>

Configure Activity

>

Customize C++ Support

Android NDK 编译工具CMake的使用

为了方便分析,Exceptions Support、Runtime Type Information Support 我们也同时勾选上,不过一般情况下可以不勾选。

Android NDK 编译工具CMake的使用

在 Customize C++ Support中,我们可以自定义一下内容:

  • C++ Standard :使用下拉列表选择你希望使用哪种 C++ 标准。选择 Toolchain Default 会使用默认的 CMake 设置。
  • Exceptions Support :如果你希望启用对 C++ 异常处理的支持,请选中此复选框。如果启用此复选框,Android Studio 会将

    -fexceptions

    标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake。
  • Runtime Type Information Support :如果你希望支持 RTTI,请选中此复选框。如果启用此复选框,Android Studio 会将

    -frtti

    标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake。

3.分析项目结构

先切换视图到

Project

方便查看。

Android NDK 编译工具CMake的使用
Android NDK 编译工具CMake的使用

对比普通新建项目的内容,可以发现示例有4个地方发生了变化:

  1. 添加了

    cpp

    目录,里面有示例 C++ 源文件

    native-lib.cpp

    ,它的代码里面提供了一个简单的 C++ 函数 stringFromJNI(),此函数可以返回字符串“Hello from C++”。
  2. MainActivity.java

    中添加了两个部分,一个负责加载so库;一个则为native方法。
  3. build.gradle

    中也添加了两个部分,一个为C++添加异常处理、RTTI的处理的支持;一个则为CMake封装构建配置,同时也提供一个相对路径给CMake的构建脚本

    CMakeLists.txt

    ;
  4. 添加了

    CMakeLists.txt

    脚本文件,它的主要作用是定义了哪些文件需要编译以及和其他库的关系,即辅助Cmake进行系列的操作。

注:native相关方法去掉报红

取消检测即可,打开 Settings>Editor>Inspections>Android>Missing JNI function 去掉勾选。

Android NDK 编译工具CMake的使用

新的项目完成创建后,我们可以清晰地看到结构及内容上的变化:

3.1 so库/native方法

MainActivity.java

public class MainActivity extends AppCompatActivity {

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

    @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();
}
           

下面说明一下相关的内容:

  • System.loadLibrary(“native-lib”)负责加载动态的原生库文件,

    native-lib

    CMakeLists.txt

    中定义的library。

CMake 使用以下规范来为库文件命名:

格式:

lib库名称.so

注:如果你在构建脚本中指定“native-lib”作为共享库的名称,CMake 将创建一个名称为

libnative-lib.so

的文件。不过,在 Java 代码中加载此库时,请使用你在 CMake 构建脚本中指定的名称。

  • stringFromJNI为native方法与

    native-lib.cpp

    的Java_com_brainbg_nkdcmakeall_MainActivity_stringFromJNI的相对应的。

3.2 原生源文件

native-lib.cpp

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

extern "C" JNIEXPORT jstring JNICALL
Java_com_brainbg_nkdcmakeall_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
           

其中

Java_com_brainbg_nkdcmakeall_MainActivity_stringFromJNI

是根据

MainActivity.java

stringFromJNI()

生成的,其格式如下:

格式:Java_包名_类名_方法名

3.3 构建配置

这部分主要是关于Cmake、C++相关的构建配置。

build.gradle

android {
    defaultConfig {
    ......
    //Exceptions Support(-fexceptions)、Runtime Type Information (-frtti) 构建关联
        externalNativeBuild {
            cmake {
                cppFlags "-frtti -fexceptions"
            }
        }
    }

    //封装CMake构建配置
    externalNativeBuild {
         //提供一个相对路径给CMake构建脚本
        cmake {
            path "CMakeLists.txt"
        }
    }
}
           

对比ndk-build和CMake的配置

//CMake需要关联CMakeLists.txt
 externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }


//ndk-build需要关联Android.mk
 externalNativeBuild {
        ndkBuild {
            path file('jni/Android.mk')
        }
    }

           

3.4 脚本配置

CMakeLists.txt

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-lib.cpp )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
           

上面的配置简单说明下:

  • cmake_minimum_required:CMake所需的最低版本要求,目前为3.4.1。
  • add_library:
    • Sets the name of the library:设置库的名称。例如指定

      native-lib

      作为共享库的名称,那么CMake 将创建一个名称为

      libnative-lib.so

      的库文件。
    • Sets the library as a shared library:将库设置为共享库.
    • Provides a relative path to your source file(s):提供源文件的相对路径
  • find_library:
  • target_link_libraries:

更多关于Cmake的用法可以查看CMake文档

3.5 运行流程

下面为官方NDK文档的流程说明:

  1. Gradle

    调用您的外部构建脚本

    CMakeLists.txt

  2. CMake

    按照构建脚本中的命令将 C++ 源文件

    native-lib.cpp

    编译到共享的对象库中,并命名为

    libnative-lib.so

    ,Gradle 随后会将其打包到 APK 中。
  3. 运行时,应用的

    MainActivity

    会使用

    System.loadLibrary()

    加载原生库。现在,应用可以使用库的原生函数

    stringFromJNI()

  4. MainActivity.onCreate()

    调用

    stringFromJNI()

    ,这将返回“Hello from C++”并使用这些文字更新

    TextView

根据本人的理解,制作了一张简单流程图:

Android NDK 编译工具CMake的使用

参考资料

https://github.com/googlesamples/android-ndk/tree/master/hello-libs

https://github.com/android-ndk/ndk

https://developer.android.google.cn/studio/projects/add-native-code.html#link-gradle

https://cmake.org/cmake/help/latest/index.html#

Author

作者:Brainbg(白雨)

GitHub:https://github.com/Brainbg

博客:https://www.brainbg.com/

CSDN:https://blog.csdn.net/u014720022

简书:https://www.jianshu.com/u/94518ede7100