JNI詳解------完整Demo

爲什麼要用JNI?因爲有些功能JAVA無法提供,比如對掃描儀驅動,我現在就是要搞這個,網上給的例子都是SB.我氣不過,便要自己去搞.感覺很悲劇.搜來想去,只能想辦法通過C/C++來操作,然後用JAVA去調用C.這就需要JNI了.

 

什麼是JNI?

JNI是Java Native Interface的縮寫,它提供了若干的API實現了Java和其他語言的通信(主要是C&C++).這是百度百科上說的.通俗來說,就是JAVA調用C/C++函數的接口.如果你要想調用C系列的函數,你就必須遵守這樣的約定.

JNI接口都長什麼樣?

就一個native的關鍵字.

public  class NativeDemo {
	{
		/**
		 * 系統加載其他的語言的函數
		 */
		System.load("C:\\Users\\Administrator\\Desktop\\com\\Hello.dll");
	}
	/**
	 * 就這個natice關鍵字.標記了這個接口,看起來像是abstract
	 */
	public native void sayHello();
	
	
	public static void main(String[] args) {
		new NativeDemo().sayHello();
	}
}

靜態代碼塊先不講。先看native方法。

sayHello()方法加了一個關鍵字native,就代表是一個native接口.執行這個方法時,會根據jni.h來找到真正的C來編寫的sayHello()的實際函數.

jni.h是什麼?

它實際上就存在%JAVA_HOME%\bin\include下面的一個文件,另外還有個%JAVA_HOME%\bin\include\win32下的jni_md.h.

這東西不說其他的作用,我也不清楚,只知道它裏面存儲了大量的函數和對象,它有個很好的方法就是通過native接口名,獲取C函數.

打個比方類似如下:

public static String getCMethod(String javaMethodName);

它可以根據你的java接口,找到C函數並調用.

但這就意味着,你不能在C裏隨意寫函數名,因爲如果你寫的java方法叫native aaa();C函數也叫aaa();但jni.h通過getCMethod(String javaMethodName)去找的結果是xxx();那這樣就無法調用了.

既然不能隨意寫,怎麼辦?

沒事,jdk提供了一個通過java方法生成c函數接口名的工具javah.

javah是什麼?

就像java是運行main方法一樣,javah就是提供具有native method的java對象的c函數接口.

dos命令如下:

javac NativeDemo.java
javah NativeDemo

這個命令可以提供一個c函數的接口.

上面那個NativeDemo被javah了之後就生成了一個文件Hello.h(可能我改了名字),就是C函數的接口.裏面有方法名和返回值什麼的.

Hello.h長什麼樣?

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class NativeDemo */

#ifndef _Included_NativeDemo
#define _Included_NativeDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     NativeDemo
 * Method:    sayHello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_NativeDemo_sayHello
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

 

最重要的C函數接口就是這樣:JNIEXPORT void JNICALL Java_NativeDemo_sayHello(JNIEnv *, jobject);

JNIEXPORT :在Jni編程中所有本地語言實現Jni接口的方法前面都有一個"JNIEXPORT",這個可以看做是Jni的一個標誌,至今爲止沒發現它有什麼特殊的用處。

void :這個學過編程的人都知道,當然是方法的返回值了。

JNICALL :這個可以理解爲Jni 和Call兩個部分,和起來的意思就是 Jni調用XXX(後面的XXX就是JAVA的方法名)。

Java_NativeDemo_sayHello:這個就是被上一步中被調用的部分,也就是Java中的native 方法名,這裏起名字的方式比較特別,是:包名+類名+方法名。

JNIEnv * env:這個env可以看做是Jni接口本身的一個對象,jni.h頭文件中存在着大量被封裝好的函數,這些函數也是Jni編程中經常被使用到的,要想調用這些函數就需要使用JNIEnv這個對象。例如:env->GetObjectClass()。(詳情請查看jni.h)

jobject obj:代表着native方法的調用者,本例即new NativeDemo();但如果native是靜態的,那就是NativeDemo.class .

也就是說,我們的native sayHello()方法實際上是運行C的Java_NativeDemo_sayHello()這個方法,我們是不能隨意寫C函數名的的,只能這樣寫。

接下來我們可以照着接口去寫真正的函數方法了.新建Hello.cpp

/* Replace "dll.h" with the name of your header */
#include "Hello.h"
#include <windows.h>
#include <iostream>

JNIEXPORT void JNICALL Java_NativeDemo_sayHello(JNIEnv *, jobject){
	using namespace std;
	cout << "Hello___________World";
} 

這個方法什麼都沒有做,就打印了一句話"Hello___________World"

然後將Hello.h和Hello.cpp編譯運行出dll文件.生成Hello.dll

這個過程中可以能編譯會出現問題.

說“jni.h”: No such file or directory:

你需要把jdk裏面的那倆jni.h給拷貝過來,再編譯,如果還出錯.需要把Hello.h裏的頭部#include<jni.h> 改寫成 #include "jni.h".由尖括號改成雙引號,具體咋回事,咱不是C程序員不清楚.

運行下試試吧

這樣你現在就擁有了下列文件

實際上你現在就只需要NativeDemo和Hello.dll就行了。

別急,還有一個。NativeDemo裏的靜態代碼塊請準確把dll庫給load進去

System.load("C:\\Users\\Administrator\\Desktop\\com\\Hello.dll");

然後編譯java,運行

C:\Users\Administrator\Desktop\com>javac NativeDemo.java

C:\Users\Administrator\Desktop\com>java NativeDemo
Hello___________World
C:\Users\Administrator\Desktop\com>

非常完美。C++裏的打印被調用了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章