### 引言
在學習了C語言基礎之后 ,我們簡單的了解了C語言編程的一些范式 , 了解了指針 , 結構體 , 聯合體 , 函數 , 文件IO等等 。我們最終的目的是要學會NDK開發 , 而NDK開發就離不開我們的JNI技術 。下面 , 就來開始我們的JNI之旅吧 。
### JNI的概念
JNI全稱 Java Native Interface , java本地化接口 , 可以通過JNI調用系統提供的API , 我們知道 , 不管是linux還是windows亦或是mac os , 這些操作系統 , 都是依靠C/C++寫出來的 , 還包括一些匯編語言寫的底層硬件驅動 。java和C/C++不同 , 它不會直接編譯成平臺機器碼,而是編譯成虛擬機可以運行的java字節碼的.class文件,通過JIT技術即時編譯成本地機器碼,所以有效率就比不上C/C++代碼,JNI技術就解決了這一痛點,下面我們來看看JNI調用示意圖:

從上圖可以得知 ,JNI技術通過JVM調用到各個平臺的API , 雖然JNI可以調用C/C++ , 但是JNI調用還是比C/C++編寫的原生應用還是要慢一點 , 不過對高性能計算來說 , 這點算不得什么 , 享受它的便利 , 也要承擔它的弊端 。
### JNI開發流程
因為JNI開發并未涉及到NDK , 所有我們的開發工具是eclipse and visual studio 。
> 第一步:編寫java本地方法
```java
public static native String getStringFromC() ;
```
> 第二步:生成.h頭文件 , 需要使用到的java命令是javah
```
# 假定你以配置好java環境
# 在控制臺中進入工程src目錄
> cd E:\java_workspace\Hello_JNI\src
> javah com.zeno.jni.HelloJni
# 需要注意的是 , com.zeno.jni.HelloJni , 是全類名, 包名.類名
```
> 第三步:將.h頭文件復制到VS的代碼文件目錄下 , 在解決方案中的頭文件目錄-> 右鍵-> 添加 -> 添加現有項 。 將我們的頭文件添加進來。

添加進來之后 , 打開我們的頭文件 , 會發現 , 頭文件里面的jni.h這個頭文件找不到 , 因為它不是系統的頭文件 , 所有我們需要將它找出來 , 并復制到我們的工程項目中 。
```
# JDK 的jni.h頭文件目錄
D:\Java\jdk1.8.0_60\include\jni.h
# 在jni.h頭文件中,又引入了jni_md.h頭文件 , 所有這個我們也要一并賦值過來
D:\Java\jdk1.8.0_60\include\win32\jni_md.h
```
將jni.h這個頭文件按照上述步驟 , 添加到頭文件目錄中 , 注意將`<>`改成`" "` , `<>`表示引入的是系統頭文件,`" "`表示引入的是第三方頭文件。
> 第四步:實現頭文件
```c
// 生成的頭文件函數
/*
* Class: com_zeno_jni_HelloJni
* Method: getStringFormC
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_zeno_jni_HelloJni_getStringFromC
(JNIEnv *, jclass);
```
新建一個.c的文件 ,引入我們生成的頭文件 ,然后實現我們生成的C語言函數。
```c
/*
* Class: com_zeno_jni_HelloJni
* Method: getStringFormC
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_zeno_jni_HelloJni_getStringFromC
(JNIEnv *Env, jclass jclazz) {
return (*Env)->NewStringUTF(Env, "Jni C String");
}
```
> 第五步:生成動態鏈接庫
補充:
|庫名稱|特性|擴展名|
|:-----:|:----:|:----:|
|動態庫|1.動態庫把對一些庫函數的鏈接載入推遲到程序運行的時期。2.可以實現進程之間的資源共享。(因此動態庫也稱為共享庫)3.可以動態注入到程序中|win(.dll)linux(.so)|
|靜態庫|1.靜態庫對函數庫的鏈接是放在編譯時期完成的。2.程序在運行時與函數庫再無瓜葛,移植方便。3.浪費空間和資源,因為所有相關的目標文件與牽涉到的函數庫被鏈接合成一個可執行文件。|win(.lib)linux(.a)|
了解了靜態庫和動態庫 , 下面我們就來生成一個動態庫,以VS為例:
```
選中項目 -> 右鍵 -> 屬性 -> 常規 -> 項目默認值 -> 配置類型 , 選擇動態庫.dll
```

配置完成之后 , 選中項目 -> 生成 。即可生成動態鏈接庫。

> 第六步:配置環境變量
我們生成了.dll文件之后 , java環境并不知道有這個.dll動態鏈接庫的存在 , 所有我們需要將生成.dll的文件目錄 , 配置到環境變量中。

### 注意:配置好環境變量 , 需要重新啟動eclipse ,不然會找不到動態鏈接庫的。
> 第七步:加載動態鏈接庫
```java
static{
System.loadLibrary("Hello_JNI") ;
}
```
> 第八步 : 調用本地方法并執行
```java
/**
*
* @author Zeno
*
* JNI (Java Native Interface) java本地化接口
*
* Android Framework層與Native層相互通信的基石
*
*
*/
public class HelloJni {
// java調用C/C++函數的本地方法
public static native String getStringFromC() ;
public static void main(String[] args) {
System.out.println("getStringFormC == "+getStringFromC());
}
static{
// 加載動態庫
System.loadLibrary("Hello_JNI") ;
}
}
```
輸出:
```java
getStringFormC == Jni C String
```
JNI 開發的步驟雖然多 , 但大多比較簡單 , 操作起來不難 。
### 結語
這篇是JNI系列的開篇 , 總體來說比較簡單 , 先將流程走順了 , 后續的開發就會好很多 , 流程性的東西 , 需要多操作 , 就會形成固定的套路 。
- 簡介
- C語言基礎及指針①語法基礎
- C語言基礎及指針②之指針內存分析
- C語言基礎及指針③函數與二級指針
- C語言基礎及指針④函數指針
- C語言基礎及指針⑤動態內存分配
- C語言基礎及指針⑥字符操作
- C語言基礎及指針⑦結構體與指針
- C語言基礎及指針⑧文件IO
- C語言基礎及指針⑨聯合體與枚舉
- C語言基礎及指針⑩預編譯及jni.h分析
- JNI開發系列①JNI概念及開發流程
- JNI開發系列②.h頭文件分析
- JNI開發系列③C語言調用Java字段與方法
- JNI開發系列④C語言調用構造方法
- JNI開發系列⑤對象引用的處理
- NDK開發基礎①使用Android Studio編寫NDK
- NDK開發基礎②文件加密解密與分割合并
- NDK開發基礎③增量更新之服務器端生成差分包
- C++基礎①命名空間結構體和引用