Android NDK开发(二)——CMake脚本构建项目

在Android Studio 2.2 之后,工具中增加了CMake的支持,也就是说,Android Studio 2.2 之后有两种选择来编译C/C++代码,一个是上篇ndk-build + Android.mk + Application.mk组合编译,另一个是CMake + CMakeList.txt组合构建项目

CMake的使用

1、配置环境

Android NDK开发(二)——CMake脚本构建项目_第1张图片

2、创建CMake工程

方式一、在工程创建的时候添加

使用AS3.0新建一个NDKDemo工程,注意勾选Include C++ support

创建成功后,可以看到和普通的Android有以下4个不同:

  • main 下面增加了 cpp 目录,即放置 C/C++ 代码的地方
  • Module下的 build.gradle 有修改
  • 增加了 CMakeLists.txt 文件
  • 多了一个 .externalNativeBuild 目录

app/build.gradle下多了如下配置:

android {
    ...
    defaultConfig {
        ...
        externalNativeBuild {
            cmake {
                //主要填写CMake的命令参数
                cppFlags ""
            }
        }
        ...
    }
    ...
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"// cmake配置文件路径
        }
    }
}

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.

# 配置so库信息
add_library( # Sets the name of the library.
             # 生成的so库名称,此处生成的so文件名称是libnative-lib.so
             native-lib

             # Sets the library as a shared library.
             # STATIC:静态库,是目标文件的归档文件,在链接其他目标的时候使用
             # SHARED:动态库,会被动态链接,在运行时被加载
             # MODULE:模块库,是不会被链接到其他目标中的插件,但是可能会在运行时使用dlopen-系列的函数动态链接
             SHARED

             # Provides a relative path to your source file(s).
             # 资源文件,可以多个
             # 资源路径时相对路径,相对于本CMakeList.txt所在目录
             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.
              # android系统每个类型的库会存放一个特定的位置,而log库存放在log-lib中
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              # android系统在c环境下打log到logcat的库
              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} )

我们发现系统已经帮我们把native-lib.cpp源文件写好了,根据函数名可以知道,函数的native申明在MainActivity中

#include 
#include 

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

再看看MainActivity文件,与以前的jni调用方式完全一样

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");//加载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());//jni调用
    }

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

现在我们在MainActivity中再申明一个native方法,光标放在函数名上,然后alt + enter

Android NDK开发(二)——CMake脚本构建项目_第2张图片

选择创建jni方法的选项,确认后会发现再native-lib.cpp中发现对应的本地代码框架已经写好

extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_jin_ende_1test_MainActivity_getStringFromJNI(JNIEnv *env, jobject instance) {

    // TODO
    
    return env->NewStringUTF(returnValue);
}

方式二:在项目开发过程中添加

有时候,我们的项目已经在进行中或者维护中,突然需要使用jni调用咋办?Android Studio也提供了相应的方法。

首先在app/src/main/jni下创建native-lib.c文件:

Android NDK开发(二)——CMake脚本构建项目_第3张图片

然后在app目录下新建一个CMakeLists.txt:

Android NDK开发(二)——CMake脚本构建项目_第4张图片

 

接着配置模块支持jni调用,项目模块右键:

Android NDK开发(二)——CMake脚本构建项目_第5张图片

Android NDK开发(二)——CMake脚本构建项目_第6张图片

点击ok后,系统开始编译,编译成功后build.gradle发生如下变化:

Android NDK开发(二)——CMake脚本构建项目_第7张图片

然后我们新建一个java调用本地代码的工具类JNIUtils.class,加载库,申明本地方法,选中方法alt+enter

Android NDK开发(二)——CMake脚本构建项目_第8张图片

发现自动在native-lib.c中为我们创建了本地代码的框架:

Android NDK开发(二)——CMake脚本构建项目_第9张图片

我们完善native-lib.c代码内容如下:

#include 

JNIEXPORT jstring JNICALL
Java_com_example_jin_jnidemo2_JNIUtils_getStringFromJNI(JNIEnv *env, jobject instance) {

    return (*env)->NewStringUTF(env, "Hello from C++");
}

上述两种方式中,实现JNI方法的区别在于,一个是用C++来实现,一个是用C来实现,它们的区别主要集中在对 env 的操作上,其他都是类似的,如下所示:

C++:  env->NewStringUTF("Hello from C++");
C:  (*env)->NewStringUTF(env,"Hello from C++");

 

你可能感兴趣的:(Android)