# Java 基础 - Java native方法以及JNI实践

\[TOC]

> 转载：[Java native方法以及JNI实践](https://www.jianshu.com/p/1ba925157f7d)

## 1. 前言

今天看AndFix实现时，核心方法之ReplaceMethod方法是一个native方法，之前并没有遇到过，所以在此整理记录。

### 1.1 native的作用

总而言之：native是与C++联合开发的时候用的！使用native关键字说明这个方法是原生函数，也就是这个方法是用C/C++语言实现的，并且被编译成了DLL，由java去调用。

1. native 是用做java 和其他语言（如c++）进行协作时使用的，也就是native 后的函数的实现不是用java写的。
2. 既然都不是java，那就别管它的源代码了，我们只需要知道这个方法已经被实现即可。
3. native的意思就是通知操作系统， 这个函数你必须给我实现，因为我要使用。 所以native关键字的函数都是操作系统实现的， java只能调用。
4. java是跨平台的语言，既然是跨了平台，所付出的代价就是牺牲一些对底层的控制，而java要实现对底层的控制，就需要一些其他语言的帮助，这个就是native的作用了。

## 2. JNI简介

native方法是通过java中的JNI实现的。JNI是Java Native Interface的 缩写。从Java 1.1开始，Java Native Interface (JNI)标准成为java平台的一部分，它允许Java代码和其他语言写的代码进行交互。

JNI一开始是为了本地已编译语言，尤其是C和C++而设计 的，但是它并不妨碍你使用其他语言，只要调用约定受支持就可以了。

目前java与dll交互的技术主要有3种：jni，jawin和jacob。

目前功能性而言：jni >> jawin > jacob，其大致的结构如下图：

![2020-07-06-yPvDfb](https://image.ldbmcs.com/2020-07-06-yPvDfb.jpg)

windows，基于native的PE结构，windows的jvm基于native结构，Java的应用体系构建于jvm之上。jvm通过加载此jni程序间接调用目标原生函数。

![2020-07-06-pI5u1u](https://image.ldbmcs.com/2020-07-06-pI5u1u.jpg)

### 2.1 JNI的生成步骤——Mac版

1. 编写带有native声明的方法的java类，生成.java文件
2. 使用javac命令编译所编写的java类，生成.class文件
3. 使用javah -jni java类名生成扩展名为h的头文件，也即生成.h文件
4. 使用C/C++（或者其他编程想语言）实现本地方法，创建.h文件的实现，也就是创建.cpp文件实现.h文件中的方法
5. 将C/C++编写的文件生成动态连接库，生成jnilib文件

## 3. JNI实例

接下来我们按照上述步骤一个一个来生成JNI实例。

### 3.1 编写带有native声明的java类，HelloWorld.java

```java
public class HelloWorld {
    public native void sayHelloWorld(); //申明一个native方法

    static{
        System.loadLibrary("HelloWorldImpl"); //装入动态链接库,"HelloWorldImpl"是装入动态链接库的名称
    }

    public static void main(String[] args){
        HelloWorld helloWorld = new HelloWorld();
        helloWorld.sayHelloWorld();
    }
}
```

### 3.2 使用javac生成HelloWorld.class

```java
javac HelloWorld.java
```

### 3.3 使用javah -jni java类生成扩展名为h的头文件

```java
javah -jni HelloWorld
```

**此处需要注意的是，我们应该在包名的根目录下执行，因为生成的文件的文件名需要引用包目录。不然就会报找不到类文件的错误**。

生成com\_think\_jni\_HelloWorld.h文件：

```c
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_think_jni_HelloWorld */

#ifndef _Included_com_think_jni_HelloWorld
#define _Included_com_think_jni_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_think_jni_HelloWorld
 * Method:    displayHelloWorld
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_think_jni_HelloWorld_displayHelloWorld
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

```

这里我们可以这样理解：这个h文件相当于我们在java里面的接口，这里声明了一个 Java\_HelloWorld\_displayHelloWorld (JNIEnv \*, jobject);方法，然后在我们的本地方法里面实现这个方法，也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致。

### 3.4 使用C/C++实现本地方法

创建HelloWorldImpl.cpp，代码如下所示：

```c
#include "jni.h"
#include "com_think_jni_HelloWorld.h"
#include <stdio.h>
JNIEXPORT void JNICALL Java_com_think_jni_HelloWorld_displayHelloWorld(JNIEnv *env,jobject obj){
    printf("Hello World!\n");
    return;
}
```

### 3.5 将本地方法编写的文件生成动态链接库

```c
gcc -I/Library/Java/JavaVirtualMachines/[根据装的jdk版本来定]/Contents/Home/include/ -dynamiclib HelloWorldImpl.cpp -o libhell.jnilib
```

但是会出现运行这个命令报错：

![2020-07-06-ddKseT](https://image.ldbmcs.com/2020-07-06-ddKseT.jpg)

我们找到这个文件在：

```shell
/Library/Java/JavaVirtualMachines/[根据安装的jdk版本来定]/Contents/Home/include/darwin/
```

这个目录下，所以我们再链接该目录到以上命令中：

```bash
gcc -I/Library/Java/JavaVirtualMachines/jdk1.8.0_71.jdk/Contents/Home/include/ -I/Library/Java/JavaVirtualMachines/jdk1.8.0_71

.jdk/Contents/Home/include/darwin/ -dynamiclib HelloWorldImpl.cpp -o libhell.jnilib
```

最终成功生成libhell.jnilib。

### 3.6 运行可执行文件

把上述文件放在同一个文件夹后，执行：

```java
java HelloWorld
```

即可看到：

![2020-07-06-xUtQ3T](https://image.ldbmcs.com/2020-07-06-xUtQ3T.jpg)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ldbmcs.gitbook.io/java/java-24/java-ji-chu/java-native-fang-fa-yi-ji-jni-shi-jian.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
