NDK开发(二)————CMake构建NDK

    上期在NDK开发(一)————如何在Android Studio下进行NDK开发中我们提到android.useDeprecatedNdk=true已经不再被支持了,于是我们根据IDE的提示换成了android.deprecatedNdkCompileLease=1515317190556当然,这其实也紧紧是暂时的解决方案,因为在Android Studio2.2后,AS完善了对C/C++代码的支持,通过利用Cmake来构建NDK进行相应的开发,相比原来利用ndk-bulid构建NDK变得更加的方便快捷,其实主要体现在强大的C/C++的代码提示功能。所以这次我们重点介绍怎么样利用Cmake来构建NDK

   注意:如果你打算利用Cmake来构建NDK,那必须保证你的Android Studio版本在2.2以上,并且同时需要配合使用 Android Plugin for Gradle 2.2.0 及以上的版本。

   下面介绍具体的操作过程:

    1.新建一个Android项目My Application。注意,在AS2.2以上的版本中新建项目的时候,你会发现在最下面多出了一个Include C++ Support的选项,我们把它勾选上。剩下的一路绿灯“next”就可以了。


   由名字我们就可以看出,如果在新建项目的时候勾选了这个选项,那么我们的项目就支持 native code(C/C++)。看一看新建后的项目目录结构和平时有什么不同:

                                                         

可以看到在src目录下多了一个CmakeList.txt的文件,同时在main目录下多了一个cpp的文件夹。这个cpp文件夹中就存放我们编写的C/C++代码,而CmakeList.txt可以简单的理解为与Cmake相关的配置文件,CMake就是根据CmakeList.txt里面的具体信息去决定怎么编译和构建我们的C/C++文件。

    2.从菜单栏中选择 Tools ---> Android ---> SDK Manager,看一看是否已经把CMake和LLDB安装好了,如下图:

    

我的因为之前已经下载好了,所以后面都显示的是Installed,你如果还没下载,勾选这两项,点击Apply下载并安装就好了。这里简单解释一下CMake和LLDB的概念:

    CMake:CMake就是一个“跨平台”的编译工具。它的主要用途是能通过一系列的“源码”和“相关的配置文件”来生成相应编译器平台上的项目文件。这么解释你可能感觉还是比较抽象,其实关于CMake涉及的知识还是很多的,不过我们目前几乎用不到。你可以简单粗暴的理解为它就是一个“工具”,什么工具呢?就是在我们用AS进行NDK开发的时候,帮我们把本地的C/C++代码生成相应的动态库(XXX.so文件)的一个工具。

    LLDB:Android Studio上面调试本地代码的工具。我们进行NDK开发,编写大量的C/C++代码肯定是避免不了的,当然也肯定少不了对C/C++代码的调试。想要在AS友好的调试C/C++代码,那么这个工具是少不了的。

    3.未我们刚才新建的项目My Application配置NDK。Ctrl + shift + alt + s打开Project Structrue把我们已经下载好的NDK配置进去。



 注意这里有一点,如果你的NDK版本太低的话,在编译的时候可能出现像下面这样的错误:CMAKE_CXX_COMPILER not set,after EnableLanguage


这个错误是由于我之前一直使用的NDK是r11c这个版本导致的。打开SDK Manager把最新的NDK下载下来并配置到项目中就好了。

    4.到此为止如果不出什么意外,我们就可以运行一下项目看看具体的效果了。如果出现下图的效果,说明你成功的利用CMake构建了NDK。

                                                            

    不对呀,我一行代码还没写呢,这就OK了?没错,我们这里就是先简单的梳理一下利用CMake构建NDk的整个流程。我们在新建项目时,勾选Include C++ Support的时候,AS会制动为我们在cpp目录下生成一个native-lib.cpp的文件,可以看到函数名称是stringFromJNI。当然以后你在进行其他相关项目的开发时,大量的native code是需要你自己写的。

native-lib.cpp

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

extern "C"
JNIEXPORT jstring

JNICALL
Java_com_example_zhangxudong_myapplication_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
```
    同样MainActivity中的代码也变了样:

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

```
可以看到它在其中声明了一个native的方法stringFromJNI,这个方法的具体实现就是上面的native-lib.cpp。然后利用一个静态代码块去加载我们在编译项目时生成的动态库native-lib。最后通过调用stringFromJNI把C++代码中写的那句Hello From C++ 呈现在TextView上。现在我们再来看看CMakeLists.txt这个构建脚本文件中的内容。CMake就是根据下面的指令去编译一个 C++ 源文件,也就是 native-lib.cpp,并将编译后的产物放到共享对象库中,并将其命名为 libnative-lib.so,然后利用Gradle 将其打包到 APK 中。

```
# 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_minimim_required(VERSION 3.4.1)
```

用来设置在编译本地库时我们需要的最小的cmake版本,Android Studio自动生成,我们不需要管。

```
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 )
```

add_library用来设置编译生成的动态库的名字为native-libSHARED表示编译生成的是动态链接库(就是.so文件,静态库一般为.a文件),src/main/cpp/native-lib.cpp表示参与编译的文件的路径,这里面可以写多个文件的路径。

```
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 )
```
find_library 是用来添加一些在编译我们的本地动态库的时候需要依赖的别的一些库,由于cmake已经知道系统库的路径,所以我们这里只是指定使用log库,而不需要指定其具体的路径。然后给log库起别名为log-lib便于我们后面引用,此处的log库是我们后面调试时需要用来打log日志的库,是NDK为我们提供的。
```
target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
```
target_link_libraries 是为了关联我们自己的库和一些第三方库或者系统库,这里把我们把自己的库native-lib库和log库关联起来。上面写我们自己库的名字,下面的${XXX}里面写要关联的第三方或系统库的名字。

   还有最后一点,我们在app层下的build.gradle中会看到多了如下代码,具体作用就是将 Gradle 与你的本地库相关联并设置一些相应的配置。

```
externalNativeBuild {
            cmake {
                cppFlags ""
                //配置生成.so库的目标平台
                abiFilters "armeabi-v7a" , "armeabi"
            }
        }
    
    
    externalNativeBuild {
        cmake {
            //用来配置CMakeLists.txt的路径
            path "CMakeLists.txt"
        }
    }
```

关于利用CMake构建NDK就先讲到这里。


猜你喜欢

转载自blog.csdn.net/Hi_Red_Beetle/article/details/79052169
ndk