Java學習記錄之JNI相關

java調用c語言主要通過下面這三個步驟來實現的

  1. 加載c庫
  2. 找到對應的c函數,有對應的映射規則
  3. 調用函數

在這裏插入圖片描述

實際的例子

  1. TestJni.java 文件
  2. testJni.c 文件

TestJni.java 源代碼

public class TestJni {
	static {//1. 使用靜態代碼塊加載 c庫
		System.loadLibrary("testJni"); /* libtestJni.so */
	}

	public static void main (String args[]) {
		// 2. java 源碼中 映射到需要調用的 c函數
		
		//調用相應的函數
		test_demo();
	}
}

在java中調用 c 語言中的 test_demo 函數,有兩種方式

  1. native static 聲明
    public native static void test_demo();
    不加native 編譯會報錯
TestJni.java:7: error: missing method body, or declare abstract
        public static void test_demo();
                           ^
1 error
public class TestJni {
	static {//1. 使用靜態代碼塊加載 c庫
		System.loadLibrary("testJni"); /* libtestJni.so */
	}
	public native static void test_demo();
	public static void main (String args[]) {
		// 2. java 源碼中 映射到需要調用的 c函數
		
		//調用相應的函數
		test_demo();
	}
}
  1. 如果不加 static 不能直接直接調用,需要使用 —》類.test_demo() 來調用
public class TestJni {
	static {//1. 使用靜態代碼塊加載 c庫
		System.loadLibrary("testJni"); /* libtestJni.so */
	}
	public native void test_demo();
	public static void main (String args[]) {
		TestJni t = new TestJni();
		// 2. java 源碼中 映射到需要調用的 c函數
		
		//調用相應的函數,如果加了static,直接使用test_demo()即可調用
		t.test_demo();
	}
}

testJni.c 源代碼

#inlcude <stdio.h>

void test_demo()
{
	printf("func:%s, line:%d\n", __func__, __LINE__);
}

int main() 
{
	
	return 0;
}

那麼怎麼調用呢?

jni.pdf–>chapter 2 Getting Started

編譯java代碼的命令

javac -encoding gbk TestJni.java

生成c裏面對應的頭文件

javah -jni TestJni

會在目錄下面生成一個TestJni.h

這裏會寫c語言中函數名稱應該寫成什麼樣,其實是 類名+函數名

sgy@ubuntu:~/sgy/java_learn/jni_learn$ cat TestJni.
cat: TestJni.: No such file or directory
sgy@ubuntu:~/sgy/java_learn/jni_learn$ cat TestJni.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TestJni */

#ifndef _Included_TestJni
#define _Included_TestJni
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     TestJni
 * Method:    test_demo
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_TestJni_test_1demo
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif
sgy@ubuntu:~/sgy/java_learn/jni_learn$

修改testJni.c文件

#include <jni.h>
#include <stdio.h>
#include "TestJni.h"
JNIEXPORT void JNICALL
Java_TestJni_test_1demo(JNIEnv *env, jobject obj)
{
	printf("func:%s, line:%d\n", __func__, __LINE__);
return;
}



int main() 
{
	
	return 0;
}

編譯成動態庫

gcc -fPIC -shared -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/ -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/linux/ -o libtestJni.so testJni.c

告訴java 動態庫的路徑,需要設置環境變量

export LD_LIBRARY_PATH=.

運行java的命令

java TestJni

第二種方法, testJni.c 源碼

第一種方法,c語言的函數的名字比較固定,第二種可以比較方便的指定對應關係

jni.pdf—>8.4.1 The JNI_OnLoad Handler

在這裏插入圖片描述

void test()
{
	printf("func:%s, line:%d\n", __func__, __LINE__);
}

static const JNINativeMethod methods[] = {
	{"test_demo", "()V",  test}, 
};


JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
	JNIEnv *env;
	jclass cls;

	if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
		return JNI_ERR; /* JNI version not supported */
	}

	cls = (*env)->FindClass(env, "TestJni");
		if (cls == NULL) {
		return JNI_ERR;
	}

	if ((*env)->RegisterNatives(env, cls, methods, 1) < 0) {
		return JNI_ERR;
	} 
	return JNI_VERSION_1_4;
}
  1. cls = (*env)->FindClass(env, “JNIDemo”); 先要找到對應的類,返回一個cls
  2. (*env)->RegisterNatives(env, cls, methods, 1) 註冊對應java函數和c函數的對應關係,對應關係存在methods結構體裏面
typedef struct {
    char *name;          /* Java裏調用的函數名 */
    char *signature;    /* JNI字段描述符, 用來表示Java裏調用的函數的參數和返回值類型 */
    void *fnPtr;          /* C語言實現的本地函數 */
} JNINativeMethod;


static const JNINativeMethod methods[] = {
	{"test_demo", "()V",  test}, 
};

最後的源代碼

#include <jni.h>
#include <stdio.h>
#include "TestJni.h"
JNIEXPORT void JNICALL
Java_TestJni_test_1demo(JNIEnv *env, jobject obj)
{
	printf("func:%s, line:%d\n", __func__, __LINE__);
	return;
}

void test()
{
	printf("func:%s, line:%d\n", __func__, __LINE__);
}

static const JNINativeMethod methods[] = {
	{"test_demo", "()V",  test}, 
};


JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
	JNIEnv *env;
	jclass cls;

	if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
		return JNI_ERR; /* JNI version not supported */
	}

	cls = (*env)->FindClass(env, "TestJni");
		if (cls == NULL) {
		return JNI_ERR;
	}

	if ((*env)->RegisterNatives(env, cls, methods, 1) < 0) {
		return JNI_ERR;
	} 
	return JNI_VERSION_1_4;
}


int main() 
{
	
	return 0;
}

signature的格式和介紹
在這裏插入圖片描述
jni的字段描述符
對於參數是String類, signature應該寫成 Ljava/lang/String;
對於其他類,統一寫成 Ljava/lang/Object;

java函數怎麼傳遞參數到c函數裏面

1. 基本數據類型,直接使用,直接返回

TestJni.java 裏面的函數改成下面這個樣子

	public native static int test_demo(int java_var);
	
	System.out.println(test_demo(1));

testJni.c

jint test(JNIEnv *env, jclass cls, jint java_var)
{
	printf("func:%s, line:%d, java_var:%d\n", __func__, __LINE__, java_var);
	return 2;
}

執行結果

func:test, line:13, java_var:1
2

2. String類型參數,返回值是String類型

jni.pdf—>P39

TestJni.java 裏面的函數改成下面這個樣子

	public native static String test_demo(String java_str);

	System.out.println(test_demo("java_str"));

testJni.c

jstring test(JNIEnv *env, jclass cls, jstring java_str)
{
	const jbyte *str;
	str = (*env)->GetStringUTFChars(env, java_str, NULL);
	if (str == NULL) {
		return NULL; /* OutOfMemoryError already thrown */
	}

	
	printf("func:%s, line:%d, java_str:%s\n", __func__, __LINE__, str);

	
	return (*env)->NewStringUTF(env, "c_str");;
}

static const JNINativeMethod methods[] = {
	{"test_demo", "(Ljava/lang/String;)Ljava/lang/String;",  test}, 
};

執行結果

func:test, line:20, java_str:java_str
c_str

3. 數組類型參數,返回值是int類型

jni.pdf—>P49

TestJni.java 裏面的函數改成下面這個樣子

		public native static int test_demo(int array[]);
		int array[] = {1, 2, 3};
		System.out.println(test_demo(array));

testJni.c

jint test(JNIEnv *env, jclass cls, jintArray java_array)
{
	jint i = 0;
	jint sum = 0;
	jint *cArray;
	cArray = (*env)->GetIntArrayElements(env, java_array, NULL);

	if (cArray == NULL) {
		return 0; /* exception occurred */
	}
	for (i = 0; i < (*env)->GetArrayLength(env, java_array); i++) {
		sum += cArray[i];
	}
	(*env)->ReleaseIntArrayElements(env, java_array, cArray, 0);
	return sum;
}

static const JNINativeMethod methods[] = {
	{"test_demo", "([I)I",  test}, 
};

執行結果

6

jni關於數組提供的一些函數接口
在這裏插入圖片描述

PS:疑問

  1. 如果jni的字段描述符 java函數和C語言函數不匹配執行的時候會有什麼效果?
         執行的時候會報錯
Exception in thread "main" java.lang.NoSuchMethodError: Method TestJni.test_demo()V name or signature does not match
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章