带着问题学习

  1. 什么是 JNI?
  1. JNI 的作用是什么?
  1. JNI 怎么操作 Java 中的对象?
  1. JavaVM 与 JNIEnv 的联系与区别是什么?

什么是JNI

JNI 全称为 Java Native Interface,我们在 Android 开发中,现在一般都是基于 Java/Kotlin 进行开发,但是,我们难免需要与用 C/C++ 编写的本地方法进行交互,而我们知道,Java/Kotlin 本身并不能直接调用 C/C++ 的代码,所以,就需要用到 JNI,也就是说 JNI 就像是连接 C/C++Java/Kotlin 之间的桥梁,使得 Java/Kotlin 能间接地调用到 C/C++ 的代码

怎么使用 JNI

  • 安装 CMakeNDK
  • 创建 native C++ 项目
  • 运行

安装 CMake 和 NDK

打开 Android Studiosetting 面板,找到 Appearance&Behavior -> System Settings -> Android SDK -> SDK Tools,勾选上 NDKCMake,进行下载安装即可
notion image
notion image

创建 native C++ 项目

打开 new Project 面板,拉到最下面,选择 native C++,选择响应的 C++ 版本并创建就行
notion image

运行

创建完项目之后,可以看到,自动生成了以下几个文件,其中 CMakeLists.txtCMake 用于配置编译选项、源文件、库链接等的构建脚本文件
这时候,初始代码 Android Studio 已经生成完了,可以直接运行看效果
notion image

如何在 Java 中调用 C/C++

引入相应的库

JNI 中,所有的 C/C++ 都不是直接调用的,而是会被打包成 so 库的形式,所以,在 Java/Kotlin 中使用时,要先引入相应的库,下面来看看我们刚刚创建完的项目里面的初始化代码是怎么做的

声明方法并调用

上面已经引入了 C/C++ 的库,接下来,就需要在我们的 Java/Kotlin 代码中,声明在 C/C++ 库中实现的方法,然后直接调用即可

JNI 相关

引入

在上面创建的项目中,可以看到,自动生成了一个 .cpp 文件,其中生成了几行代码,其函数名是以 包名+文件名+方法名 构成的,下面来看看其代码
可以看到,上面的函数中,传入了 JNIEnv* envjobject 两个参数,下面,我们来讨论一下其具体作用是什么

JavaVM and JNIEnv

先来看看 Google 官方的解释
JNI defines two key data structures, "JavaVM" and "JNIEnv". Both of these are essentially pointers to pointers to function tables. (In the C++ version, they're classes with a pointer to a function table and a member function for each JNI function that indirects through the table.)
The JavaVM provides the "invocation interface" functions, which allow you to create and destroy a JavaVM. In theory you can have multiple JavaVMs per process, but Android only allows one.
The JNIEnv provides most of the JNI functions. The JNIEnv is used for thread-local storage.

JavaVM

从官方描述中可以看出,理论上,一个进程,是可以有多个 JavaVM 的,但是 Android 中,只允许一个进程拥有一个 JavaVM
JavaVM 是一个代表 Java虚拟机实例 的数据结构,具体是一个 struct 类型,可以用于获取 JVM实例 的引用,并通过它,访问一下 JVM 的函数,比如创建 Java 对象、调用 Java 方法、获取当前线程的 JNIEnv 指针等,下面来看看其具体定义:
下面是一个简单使用的例子:

JNIEnv

从官网的描述中,可以知道 JNIEnv 提供了 JNI 大部分的功能,并且是一个 Thread-Local storage,且 JNIEnv 只在创建它的线程中有效,也就是线程私有的,这一点可以和上面的 JavaVM 对比,同时,对于在 native 中创建的线程,如果需要访问我们的 JNI 方法,就需要先用 AttachCurrentThread 方法将其与现有的 JNIEnv 进行绑定,并且在不需要或者线程退出时,调用 DetachCurrentThread() 方法进行解除
从上面我们创建出来的项目中,其初始化代码,在 .cpp 文件的方法中,就传入了一个 JNIEnv 对象,这个对象提供了访问 Java运行时环境 的能力,我们可以通过这个,从而操作我们的 Java对象Java方法,还可以对 Java memory 进行管理
JavaVM 一样,JNIEnv 也是一个 struct 类型的,下面来看看其定义:
可以看到,JNIEnv 定义了很多方法,几乎包含了全部的 JNI 方法,大概可以概括为,在 C/C++ 根据我们的 Java class 调用相应的方法,并返回相应的类型

参考/推荐

 
Retrofit 初探源码Leetcode_64-最小路径和
Loading...