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

本贴最后更新于 1546 天前,其中的信息可能已经沧海桑田

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

本文链接

新建工程

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

newprojndkselectnativecpp.png

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

newprojndkcfg.png

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

打开新建好的项目。可以看到 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`给出了详细的信息。
```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。
安装完毕后,重新打开这个项目即可。

运行工程

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

newprojndkrun1.png

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

  • 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;
}

编译运行即可。
本文链接

  • Android

    Android 是一种以 Linux 为基础的开放源码操作系统,主要使用于便携设备。2005 年由 Google 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    334 引用 • 323 回帖
  • ndk
    5 引用 • 2 回帖
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    107 引用 • 153 回帖
  • App

    App(应用程序,Application 的缩写)一般指手机软件。

    91 引用 • 384 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...