? ? ? ? JNI其實是Java NativeInterface的簡稱,也就是java本地接口。它提供了若干的API實現了和Java和其他語言的通信(主要是C/C++)。
? ? ? ? Java以其跨平臺的特性深受人們喜愛,而又正由于它的跨平臺的目的,使得它和本地機器的各種內部聯系變得很少,約束了它的功能。解決Java對本地操作的一種方法就是JNI。
? ? ? ? Java通過JNI調用本地方法,而本地方法是以庫文件的形式存放的(在WINDOWS平臺上是DLL文件形式,在UNIX機器上是SO文件形式)。通過調用本地的庫文件的內部方法,使JAVA可以實現和本地機器的緊密聯系,調用系統級的各接口方法。
? ? ? ? 在項目中管理很好一般可以不使用JNI,但是有時候必須用到的時候就要掌握一下這個東西了。其實Java很多的源碼里面的一些算法都是Native聲明的,也就是這些方法很可能是在C/C++中實現了的。
? ? ? ? 我沒事做的時候自己單獨重新做了一遍JNI使用過程,分享一下流程。
1. 首先在Eclipse建立一個Java類,在這個類中用native關鍵字聲明需要用到的函數
下面是我自己寫的測試代碼
~~~
package com.ghgame.javausecpp;
public class JavaUseCpp {
public JavaUseCpp() {
System.out.println(">>>>>JNI Test Start<<<<<");
}
static {
}
public native void printStr(String str);
}
~~~
2. 在DOS窗口下生產C++的h文件
? ?這里使用命令來完成
? ?去找到Eclipse新建的工程目錄下的bin文件夾,bin存放編譯好的class文件;
? ?然后cd 到這個E:\WorkAndroid\JNITest\bin目錄
? ?在dos下輸入命令
? ?E:\WorkAndroid\JNITest\bin>javah-classpath . -jni com.ghgame.javausecpp. JavaUseCpp
? ?然后在E:\WorkAndroid\JNITest\bin即可找到一個com_ghgame_javausecpp_JavaUseCpp.h頭文件,生成成功!
注意:com.ghgame.javausecpp是包名,JavaUseCpp是類名
下面是生成的h文件截圖:

這里javah的用法:

到這里Java部分就算是差不多了。
可以打開看看剛剛生成的這個**com_ghgame_javausecpp_JavaUseCpp.h**頭文件
~~~
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_ghgame_javausecpp_JavaUseCpp */
#ifndef _Included_com_ghgame_javausecpp_JavaUseCpp
#define _Included_com_ghgame_javausecpp_JavaUseCpp
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_ghgame_javausecpp_JavaUseCpp
* Method: printStr
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_ghgame_javausecpp_JavaUseCpp_printStr
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
~~~
? ? ? 這里面注釋上標注類名、方法名、簽名(Signature),簽名這個東西下面來說。在這里最重要的是Java_com_ghgame_javausecpp_JavaUseCpp_printStr這個方法簽名。在Java端我們執行 printStr(String str)這個方法之后,JVM就會幫我們調用在DLL里的Java_com_ghgame_javausecpp_JavaUseCpp_printStr這個方法。因此我們新建一個C++source file來實現這個方法。
? ? ? ?還是扯哈簽名這個東西。
? ? ? ?為什么會有方法簽名這種東西呢?這是因為Java這邊支持函數重載,即雖然參數不一樣,但是方法名一樣,那么在JNI層它們的方法名都會是一樣的,那JNI也會犯迷糊了,得找哪個呢?
? ? ? ?不過也正是因為其參數類型是不一樣的,所以就出現了方法簽名,利用方法簽名和方法名來唯一確定一個JNI函數的調用。
? ? ? ?既然方法簽名是基于參數類型的不同而形成的,首先要知道Java各數據類型對應的簽名是什么,也就是所謂的類型簽名,
在jni.h文件中就已經定義了這樣一套規則,如下:
~~~
typedef union jvalue {
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
} jvalue;
~~~
具體不清楚的還是找度娘問其他先生。
3. 寫CPP文件,編譯出DLL
? ?建項目具體步驟就不說了。
? ?新建一個Win32Cpp就行了,里面選dll,然后把剛才編譯出來的h文件復制到C++工程的根目錄去
? ?然后新建一個**com_ghgame_javausecpp_JavaUseCpp.cpp**Cpp文件
? ?在這里面實現h文件中的方法,我這里就打印了一下Java傳過來的值而已
~~~
#include "stdafx.h"
#include "com_ghgame_javausecpp_JavaUseCpp.h"
JNIEXPORT void JNICALL Java_com_ghgame_javausecpp_JavaUseCpp_printStr(JNIEnv* env, jobject obj, jstring str)
{
const char* pTempStr = env->GetStringUTFChars(str, NULL);
printf(">>>>>>>>>>>>>>>The input string is = [ %s ]<<<<<<<<<<<<<<",pTempStr);
}
~~~
這里編譯的時候值得注意的幾個問題:
a.?需要另外兩個頭文件
? ?都在jdk的安裝目錄下,有個include文件夾,把include文件夾下面的jni.h復制到C++工程的根目錄,然后把com_ghgame_javausecpp_JavaUseCpp.h文件的#include <jni.h>改成#include "jni.h"就行了。
? ?還有個在include里面有個win32文件夾,jni_md.h這個頭文件也復制到C++工程的根目錄。
b. 在編譯項目時候要注意選擇平臺,如果你jdk,機子都是64位你就編譯64位的dll,32位同理。否則就是這個錯誤:JNI Can't load IA 32-bit .dll on a AMD 64-bit platform
編譯出來的內容就是這樣:

4. 配置Dll
? ?這里加載的方法有兩個,
a. 把剛剛生成的Dll扔到C盤Sysytem32或者SysWOW64文件夾下
b. 把C++工程下生成dll的目錄設置到環境變量path中去(我是64位的機子就把x64的配置到Path去了)

這個設置看個人喜好了
5. Java加載Dll,完成
到最后一步了,在Java里面加載Dll庫,調用c++實現的方法
~~~
package com.ghgame.javausecpp;
public class JavaUseCpp {
public JavaUseCpp() {
System.out.println(">>>>>JNI Test Start<<<<<");
}
// 加載生成的DLL庫文件
static {
System.loadLibrary("LibJniTest");
}
public native void printStr(String str);
public static void main(String[] args) {
String cName = "Ghgame";
// 傳個值試試效果
new JavaUseCpp().printStr(cName);
}
}
~~~
下面是我執行的結果:

感覺還是挺66666的。
好了,GG了,收拾東西準備下班。
- 前言
- C++讀取配置文件
- 結構體內存對齊后所占內存空間大小的計算
- do{}while(0)的妙用
- Cocos2dx實現翻牌效果(CCScaleTo與CCOrbitCamera兩種方式)
- C++的error LNK2019: 無法解析的外部符號編譯錯誤
- Java使用JNI調用C++的完整流程
- strupr與strlwr函數的實現
- strcat函數實現
- Windows上VS使用pthread重溫經典多線程賣票(pthreads-w32-2-8-0-release.exe)(windows上使用pthread.h)
- pthread的pthread_join()函數理解實驗
- 順序存儲結構和鏈式存儲結構的選擇
- C語言冒泡排序
- VS看反匯編、寄存器、內存、堆棧調用來學習程序設計
- 快速排序
- C++的構造函數初始化列表
- fatal error C1083: 無法打開包括文件: “SDKDDKVer.h”: No such file or directory
- C++實現簡單的String類