Skip to content

新建支持C/C++的工程

新建工程

新建支持C/C++的工程和我们创建工程的步骤基本一致。 在选择项目模板界面,选择Native C++卡片。

new

点击下一步,然后填入项目的基本信息。

cfg

然后一路点击下一步直至完成。

打开新建好的项目。可以看到cpp目录与java同级。 src/main/cpp中有native-lib.cppCMakeLists.txt

另外一个值得关注的是gradle文件。里面有cmake的配置。

报错: NDK not configured

若无报错请忽略本节

新建工程Build栏目出现报错信息

NDK not configured. Download it with SDK manager. Preferred NDK version is '20.0.5594570'. Log: /SampleNDK/app/.cxx/ndk_locator_record.json

信息指出NDK工具没有配置好。ndk_locator_record.json给出了详细的信息。

{
    "level": "ERROR",
    "message": "No version of NDK matched the requested version 20.0.5594570. Versions available locally: 18.1.5063045, 21.2.6472646"
}

项目要求NDK版本20.0.5594570,但电脑上没有与之匹配的版本。 我们前面安装的是21.2.6472646版本,可以在SDK Manager里下载安装NDK 20.0.5594570。 安装完毕后,重新打开这个项目即可。

运行工程

在手机上运行这个工程。可以看到效果。

构建和运行过程大致如下:

  • Gradle 调用您的外部构建脚本 CMakeLists.txt
  • CMake 按照构建脚本中的命令将 C++ 源代码文件 native-lib.cpp 编译到共享的对象库中,并将其命名为 libnative-lib.so,Gradle 随后会将后者打包到 APK 中。
  • 运行时,应用的 MainActivity 会使用 System.loadLibrary() 加载原生库。现在,应用就可以使用库的原生函数 stringFromJNI() 了。
  • MainActivity.onCreate() 会调用 stringFromJNI(),后者会返回“Hello from C++”,并通过 TextView显示出来。

观察工程

首先关注cpp目录下的文件。

CMakeLists.txt

CMakeLists.txt是构建脚本。文件中进行一些配置。

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).
             native-lib.cpp )

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,源文件是native-lib.cpp

以前可以用Android.mk来进行ndk-build。现在cmake取代了Android.mk

cpp文件

cpp文件是实现功能的地方。

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

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

注意方法的名字,Java_com_rustfisher_samplendk_MainActivity是带有Java文件的包名。 stringFromJNI是具体的方法名。

以前的ndk可以用javah生成.h头文件。现在不需要自己手动生成了。

build.gradle

gradle中指定了CMake的配置。

android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
    }
    // ...
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.10.2"
        }
    }
}

可指定此 CMake 版本作为最低版本,只需在 build.gradle 条目的末尾添加一个“+”即可,例如 3.10.2+。不过,这并非最佳做法。

Java文件

MainActivity需要加载这个库。

    static {
        System.loadLibrary("native-lib");
    }

定义关联的native方法。

public native String stringFromJNI();

.so 文件

刚才运行在armeabi-v7a架构的手机上。 编译生成的.so文件在 app/build/intermediates/cmake/debug/obj/armeabi-v7a/libnative-lib.so

CMake 库文件命名规范:lib+native-lib+.so。即 libnative-lib.so

生成release版apk时,会打包.so文件进去。

修改工程

新建的工程中,配置和方法都是as默认的。我们可以根据自己的需要来修改。

修改库名

默认的库名字是native-lib,可以自定义修改。

修改CMakeLists.txt。把库的名字改成fisher-lib,cpp文件名字改成fisher-lib.cpp

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

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             fisher-lib.cpp )

target_link_libraries( # Specifies the target library.
                       fisher-lib

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

cpp文件的名字改成fisher-lib.cpp

MainActivity文件中,修改库名。

static {
    System.loadLibrary("fisher-lib");
}

再次编译运行即可。 可以在build目录中找到libfisher-lib.so

添加native方法

仿照已有的方法,在MainActivity中添加一个native方法。

public native int getIntFromJNI();

fisher-lib.cpp文件中添加方法。注意要写上返回类型extern "C" JNIEXPORT int JNICALL

extern "C" JNIEXPORT int JNICALL
Java_com_rustfisher_samplendk_MainActivity_getIntFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    return 42;
}

编译运行即可。