JNI简介

作者: PanGeng | 来源:发表于2020-05-16 21:39 被阅读0次

@[TOC](JNI简介)

# 一、简介

## 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后面的分号,千万不要忘记了。**

相关文章

网友评论

      本文标题:JNI简介

      本文链接:https://www.haomeiwen.com/subject/necdohtx.html