# 一、简介
## 1. 什么是JNI
JNi就是java调用本地方法的技术,最简单的来说,java运行一个程序需要要和不同的系统平台打交道,在windows里就是和windows平台底层打交道,mac就是要和mac打交道,jvm就是通过大量的jni技术使得java能够在不同平台上运行。使用了这技术的一个标志就是native,如果一个类里的一个方法被native修饰,那就说明这个方法是jni来实现的,他是通过本地系统api里的方法来实现的。当然这个本地方法可能是c或者C++,当然也可能是别的语言。jni是java跨平台的基础,jvm通过在不同系统上调用不同的本地方法使得jvm可以在不同平台间移植。
![JNI UML](https://img-blog.csdnimg.cn/20191121133527673.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzIyMDkwMDcz,size_16,color_FFFFFF,t_70)
上图可以看到,JNI相当于是一层翻译器,JAVA想要调用本地方法,通过JNI解释,执行,同样的,本地方法想要调用JAVA方法,也是通过JNI来进行的。
## 2. JNI定义
- 定义:Java Native Interface,即JAVA本地接口
- 作用:使JAVA语言和其他编程语言(C、C++等)进行交互
- JNI是JAVA调用NATIVE语言的一种特性
- 实际中驱动都是C和C++开发的,通过JNI,JAVA可以调用C/C++实现的驱动,从而扩展JAVA虚拟机的能力。另外,在高效率的数学运算、游戏的实时渲染、音视频的编码和解码等方面,一般都是用C开发的
# 二、NDK是什么
## 1.定义
- 定义:Native Development Kit,是android的一个开发工具包
- 作用:快速开发C、C++的动态库,并自动将so和应用一起打包成APK
- 提供了把.so和.apk打包的工具
- NDK提供的库有限,仅拥有算法效率和敏感问题
- 提供了交叉编译器,用于生成特定的CPU平台动态库
## 2.特点
- 运行效率高
- 代码安全性高
- 功能扩展性好
- 易于代码复用和移植
# 三、JNI和NDK的关系
## 1. NDK是Android中实现JNI的手段
JNI是实现JAVA调用C/C++的途径,NDK是android中调用本地方法的桥梁。所以可以说NDK是Android中实现JNI的手段, 即在Android的开发环境中,通过NDK从而实现JNI功能。 JAVA的优点是跨平台,和操作系统之间的调用由JVM完成,但是一些和操作系统相关的操作就无法完成,JNI的出现刚刚弥补了这个缺陷,也完善了JAVA语言,将JAVA扩展的更强大。
我们需要了解的是,每个平台编译的文件后缀是不一样的
平台 | 后缀
--------| ---------
android、linux | .so
windows | .dll
mac | .a
# 四、JNI动态注册和静态注册
为了不让大家枯燥的看这些概念,我们通过两个例子来了解静态注册和动态注册。(我们在windows中运行的,所以下面例子中生成的库文件是.dll类型的)
## 1. 静态注册
<a href="https://blog.csdn.net/qq_22090073/article/details/103182987" >点击这里查看具体使用方法</a>
- 在Java中声明Native方法(即需要调用的本地方法)
- 编译上述JAVA源文件javac(得到.class文件)
- 通过javah命令导出JNI的头文件(.h文件)
- 使用JAVA需要交互的本地代码,实现在JAVA中声明的Native方法
- 编译.so库文件
- 通过JAVA命令执行JAVA程序,最终实现JAVA调用本地代码
## 2. 动态注册
在此之前我们一直在jni中使用的 Java_PACKAGENAME_CLASSNAME_METHODNAME 来进行与java方法的匹配,这种方式我们称之为静态注册。
而动态注册则意味着方法名可以不用这么长了,我们也不必再通过javah命令生成头文件,在android aosp源码中就大量的使用了动态注册的形式
```
//Java:
native void dynamicNative();
native String dynamicNative(int i);
//C++:
void dynamicNative1(JNIEnv *env, jobject jobj){
LOGE("dynamicNative1 动态注册");
}
jstring dynamicNative2(JNIEnv *env, jobject jobj,jint i){
return env->NewStringUTF("我是动态注册的dynamicNative2方法");
}
//需要动态注册的方法数组
static const JNINativeMethod mMethods[] = {
{"dynamicNative","()V", (void *)dynamicNative1},
{"dynamicNative", "(I)Ljava/lang/String;", (jstring *)dynamicNative2}
};
//需要动态注册native方法的类名
static const char* mClassName = "com/dongnao/jnitest/MainActivity";
jint JNI_OnLoad(JavaVM* vm, void* reserved){
JNIEnv* env = NULL;
//获得 JniEnv
int r = vm->GetEnv((void**) &env, JNI_VERSION_1_4);
if( r != JNI_OK){
return -1;
}
jclass mainActivityCls = env->FindClass( mClassName);
// 注册 如果小于0则注册失败
r = env->RegisterNatives(mainActivityCls,mMethods,2);
if(r != JNI_OK )
{
return -1;
}
return JNI_VERSION_1_4;
}
```
## 3. 在IDE中调用本地库的时候,需要指定java.library.path属性
- 点击右上角的Edit Configuration
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191121152552235.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzIyMDkwMDcz,size_16,color_FFFFFF,t_70)
- 在Configuration标签页中,将VM options项中的-Djava.library.path属性指定到你的dll库目录下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191121152742129.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzIyMDkwMDcz,size_16,color_FFFFFF,t_70)
## 4. 动态注册,我们需要指定java方法的方法名,方法签名,来与C函数建立关联,java方法签名是怎么获取的呢?
**下面这张表对应了java方法返回值的签名:**
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191121153433443.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzIyMDkwMDcz,size_16,color_FFFFFF,t_70)
**我们也可以通过命令来查看:**
```
使用javap命令:
javap -s -p JniTes.class
E:\java\JniTest1\src>javap -s -p Register
Compiled from "Register.java"
public class Register {
public Register();
descriptor: ()V
public native java.lang.String HelloWorld();
descriptor: ()Ljava/lang/String;
}
```
上面是终端打印出来的, 其中:
()V,代表的是Register的构造函数,是Void类型的;
()Ljava/lang/String; 是我们的本地方法,类型是String。
**特别还要注意的是String后面的分号,千万不要忘记了。**