JNI學習記錄——基礎操作與傳遞數據

最近在做的項目考慮調用WindowsAPI實現對程序內存的監控

而調用的接口是C/C++的 所以去學習了JNI的相關知識與基本的操作內容 最終還是很艱難的實現了想要實現的功能

把學習JNI的過程大致記錄了下來 包含初步的基礎操作與相互傳遞數據的操作

沒有去深入學習原理  僅僅是“實現”了需要的功能 不足之處歡迎指正~

 

第一步:編寫一個測試類

package com.tzy.test;

public class JNIDemo1 
{
	public native void JNITest1();

	public static void main(String[] args)
	{
		System.loadLibrary("TestJNI1");
		JNIDemo1 jniDemo1=new JNIDemo1();
		jniDemo1.JNITest1();
	}
}

這段代碼中,JNITest1 爲在Java程序中聲明要在C/C++代碼中實現的函數

TestJNI1則爲最終與本Java程序實現連接的.c/.cpp名 之後在windows下會生成.dll後綴的同名文件,需要將這個文件路徑導入到path中,在之後的步驟會詳細說明

 

第二步:使用javah指令生成頭文件

在該Java項目的bin目錄下使用以下指令:

javah -classpath . -jni com.tzy.test.JNIDemo1

將生成一個頭文件:

 

第三步:編寫C/C++代碼

我使用的是Visual Studio2015 編寫的是C++代碼

項目名稱與Java代碼中loadLibrary中的名字對應

勾選應用程序類型爲DLL 附加選項空項目

項目建立完成在源文件中添加類:

 

接下來將

1.第二步中生成的頭文件

2.JDK/include下的jni.h

3.JDK/include/win32下的jni_md.h

拷貝至C++項目的工程目錄下

VS生成的工程目錄TestJNI1中還有一個TestJNI1 拷貝至裏面的那個

 

拷貝完成後在頭文件中添加現有項

選中剛纔拷貝的三個頭文件

打開第二步中生成的頭文件 把報錯的#include<jni.h>改爲#include "jni.h" 表示引用外部頭文件

 

接下來編寫TestJNI.cpp,將剛纔修改的頭文件中的

實現即可

#include "TestJNI.h"
#include "com_tzy_test_JNIDemo1.h"
#include <iostream>
using namespace std;

JNIEXPORT void JNICALL Java_com_tzy_test_JNIDemo1_JNITest1
(JNIEnv *, jobject)
{
	cout << "Hello World" << endl;
}

接着在解決方案上右鍵 ——屬性

按以下方式修改

接着在項目上右鍵——生成

生成信息中包含目錄,

複製該目錄路徑,在.dll上層

然後在Java項目中bulid path——Libraries——Native library location中添加

運行Java程序

成功輸出

 

 

接下來進行傳遞數據的相應代碼改變

其他部分操作不變,僅改變相應的Java與C++代碼

首先進行一個簡單的值的傳遞示範

package com.tzy.test;

public class JNIDemo1 
{
	public native int JNITest2(int chuanruzhi);

	public static void main(String[] args)
	{
		System.loadLibrary("TestJNI2");
		JNIDemo1 jniDemo1=new JNIDemo1();
		int a=jniDemo1.JNITest2(10);
		System.out.println(a);
	}
}

在這段代碼中,把native函數返回值從void改爲int 同時傳入一個int至C++

然後主函數使用這個函數,傳入C++的值爲10 然後輸出從C++傳回的值

在javah生成的頭文件中可以看到

對比剛纔的函數

發現1.返回值由void變爲jint 參數列表中多了一個jint 這個就是Java參數中的int

兩者沒有實際區別

同樣對於boolean byte char short long float double 在C++中加上j就可以了

不同的在於字符串與數組的傳遞較爲複雜

 

接下來在TestJNI2.cpp中實現這個函數

#include "TestJNI2.h"
#include "com_tzy_test_JNIDemo1.h"
#include<iostream>


JNIEXPORT jint JNICALL Java_com_tzy_test_JNIDemo1_JNITest2
(JNIEnv *, jobject, jint input)
{
	int output = input + 1;
	return output;
}

這段代碼接收Java傳入的int 並把這個值加1之後返回給Java

然後同樣生成dll文件 在java項目中添加dll文件的上層目錄

運行 

結果如下:

 

最後寫一個字符串傳遞的例子

package com.tzy.test;

public class JNIDemo1 
{
	public native String JNITest3(String chuanruzhi);

	public static void main(String[] args)
	{
		System.loadLibrary("TestJNI3");
		JNIDemo1 jniDemo1=new JNIDemo1();
		String a=jniDemo1.JNITest3("Hello World from Java");
		System.out.println(a);
	}
}

在這段代碼中,native函數返回值改爲String 同時傳入一個String給C++

javah生成的頭文件中 函數爲如下:

可以看到string變爲了jstring 但是這個jstring與jint不同 jint可以直接當做C++中的int使用 而jstring卻不能直接當做C++中的string使用

在cpp中進行以下處理:

#include "TestJNI3.h"
#include "com_tzy_test_JNIDemo1.h"
#include<iostream>


JNIEXPORT jstring JNICALL Java_com_tzy_test_JNIDemo1_JNITest3
(JNIEnv *env, jobject, jstring input)
{
	const char* str;

	str = env->GetStringUTFChars(input, false);

	std::cout << str << std::endl;

	env->ReleaseStringUTFChars(input, str);

	char* tmpstr = "Hello World from C++";

	jstring output= env->NewStringUTF(tmpstr);

	return output;
}

利用etStringUTFChars和NewStringUTF函數進行轉換

這兩個函數都是JNI API提供的函數

這樣實現的功能是 利用C++輸出Java傳入的fromJava

再返回fromC++到Java中

生成 保存path

運行:

其他:

對於數組,會轉換成 j<類型>Array,比如int[]對應的就是jintArray 利用JNI提供的GetIntArrayElements函數進行操作

具體請參照API

若想一次返回多個類型的值,可以使用Hashmap進行傳遞,object與jobject之間進行傳遞

 

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