NDK(一):NDK 的集成

一、JNI 简介

JNI (Java Native Interface,Java本地接口)是一种编程框架,使得Java虚拟机中的Java程序可以调用本地应用/或库,也可以被其他程序调用。 本地程序一般是用其它语言(C、C++或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序。

关联文章:

要编写 JNI 就得需要集成 JNI的开发环境(NDK)。接下来我们看一下如何编写Android JNI ,以及需要的流程。

二、NDK是什么

NDK(Native Development Kit缩写)一种基于原生程序接口的软件开发工具包,可以让您在 Android 应用中利用 C 和 C++ 代码的工具。通过此工具开发的程序直接在本地运行,而不是虚拟机。

在Android中,NDK是一系列工具的集合,主要用于扩展Android SDK。NDK提供了一系列的工具可以帮助开发者快速的开发C或C++的动态库,并能自动将so和Java应用一起打包成Apk。同时,NDK还集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so文件。

三、NDK配置

Step1: NDK 相关工具的下载。
在这里插入图片描述

Step2: 配置好后,新建一个 Native 工程。
在这里插入图片描述

Step3: 选择C++的版本。

在这里插入图片描述

到这里,一个 Native 的工程就已经创建完成了。下面我们看下这个 NDK 工程的相关信息。

四、NDK 工程目录

4.1 目录结构

整体工程目录结构如下图所示,与普通的 Android 工程目录相比,Native工程目录中多了一个 cpp 目录,以及目录内对应的 CMakeList.txt 文件。

在这里插入图片描述

4.2 build.gradle

除了 cpp 文件夹的区别外,我们再来看下 build.gradle 的区别。

在这里插入图片描述

可以看到,相比普通的 Android 应用,build.gradle 配置中多了两个 externalNativeBuild 配置项。

  • defaultConfig里面的的 externalNativeBuild 主要是用于配置Cmake的命令参数。
  • 外部的 externalNativeBuild 的主要是定义了 CMake 的构建脚本 CMakeLists.txt 的路径。

4.3 CMakeLists文件

接下来我们来看一下 CMakeLists.txt 文件,CMakeLists.txt 是 CMake 的构建脚本,作用相当于 ndk-build 中的 Android.mk,代码如下。

# 设置CMake最小版本
cmake_minimum_required(VERSION 3.18.1)

# 设置项目名称
project("jnitestdemo")

# 编译library,生成链接库jnitestdemo。如果当前这个链接库与其它库有关联,可以使用target_link_libraries进行关联。
add_library(
		# 设置library名称
		jnitestdemo  
		
		# 设置library模式
        # SHARED模式会编译so文件,STATIC模式不会编译
		SHARED

        # 设置原生代码路径
        native-lib.cpp)

# 查找log的路径,并赋值给log-lib这个变量。
find_library(log-lib log)

# 将两个链接库关联到一起。
target_link_libraries(
		# 指定要关联其它lib的库,这里指当前的jnitestdemo。
        jnitestdemo

        # 将log-lib关联到jnitestdemo上(相当于是jnitestdemo依赖log-lib)。
        ${log-lib})

关于 CMake 部分的相关内容,可以查看 CMake 教程

五、工程实践

上面介绍了工程的配置部分,下面我们来具体看下项目中的代码。

JNI开发流程

  1. 编写 Java 类,声明了Native方法。
  2. 编写 Native 代码。
  3. 将 Native 代码编译成so文件。
  4. 在 Java 类中引入so库,调用 Native 方法。

其中第1,4步在Java层,第2步在Native层,第3步属于打包编译流程。

Java 层:

下面演示的是 Java 代码调用 Native 代码获取一个字符串的流程。

package com.elson.jnitestdemo;

public class MainActivity extends AppCompatActivity {
    static {
    	// 1.加载动态链接库
        System.loadLibrary("jnitestdemo");
    }
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView = findViewById(R.id.sample_text);
        // 2.通过JNI从Native层获取数据。
        textView.setText(stringFromJNI());
    }
	
	// 3.定义一个Native函数。
    public native String stringFromJNI();
}

Java层主要做了3件事。

  1. 加载动态链接库 jnitestdemo,这个库的名字在 CMakeLists.txt 文件中的 add_library() 中声明指定。
  2. 通过 JNI 从 Native 层获取 String 类型的数据。
  3. 定义一个返回String类型的 Native 函数给步骤2中使用。

Native 层:

下面看下 Native 层的代码实现。

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

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

小结:

  1. JNIEXPORT 和 JNICALL 是JNI中所定义的宏,所以需要引入 <jni.h> 这个头文件。
  2. 因为方法返回了一个 String 类型的数据,所以引入 C++ 的 <sting> 库。
  3. extern “C” 的作用:指示编译器按C语言(而不是C++)的方式进行编译。
    • C编译:C语言的方法签名只包含方法名,不包含参数,所以C语言中不存在方法重载。
    • C++编译: C++的方法签名包含方法名和含参数,所以存在方法重载。
  4. jobect对象:jobject 表示Java对象中的this,如果是静态方法则表示jclass。
  5. JNIEnv对象:代表Java调用Native层的环境,一个封装了几乎所有的JNI方法的指针。其只在创建它的线程有效,不能跨线程传递,不同的线程的JNIEnv彼此独立。

到这里,一个简单的 Native 工程就已经构建完成了,也完成了 Java 对 Native 方法的调用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值