日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JAVA进阶day07JNI(java调用c)A部分

發布時間:2023/12/9 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JAVA进阶day07JNI(java调用c)A部分 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目前接觸的JNI有java調用c和c調用java兩類。其中java調用c又有隱式和顯示兩種映射關系。本筆記針對java調用c的顯示映射。本著工程實際夠用的原則,不夠再回頭來補充。
JAVA訪問c庫需要有三個步驟
1:加載C庫
2:建立java函數名到c庫函數名的映射關系
3:在java程序里調用函數
先貼代碼,依照代碼來做分析:

public class JNIDemo {static { /* 1. load */System.loadLibrary("native"); /* libnative.so */}public native void hello();public static void main (String args[]) {JNIDemo d = new JNIDemo(); /* 2. map java hello <-->c c_hello *//* 3. call */d.hello();} }

分條縷析做筆記:

  • 1:加載C庫和在java程序里調用函數
static { /* 1. load */System.loadLibrary("native"); /* libnative.so */}

代碼中在靜態代碼塊中進行加載c庫。可以知道我們要加載的是libnative.so庫。放到靜態代碼塊中可以保證該加載能夠只執行一次。非常棒的處理。

public native void hello();

native修飾的hello表明這個hello方法不是在java中實現的而是在本地語言中實現的。我們在使用hello函數的時候首先要進行聲明。這個聲明也可以聲明為靜態的,比如public native static void hello();這時候就沒有必要先實現JNIDemo的實例化靜態對象了,也就是說我們可以省掉JNIDemo d=new JNI Demo();這個處理,直接使用JNIDemo.hello();當然我們的main函數也是在JNIDemo類中的,可以直接寫為hello()。問題又來了,那么同樣的道理為什么不是靜態的還需要實現JNIDemo對象呢,這個也是在JNIDemo的main函數中啊?這個就沒必要較真了,記住就好,又不是讓你去創造java語言,你只不過是用這個工具而已。我還發現這個public native static void hello()跟public static native void hello()是一個效果。無礙乎這個static跟native的前后。到此我們對這個加載c庫就算是掌握了。

  • 2:建立java函數名到c庫函數名的映射關系
    為了將java中的hello跟c語言中的函數對應起來,這部分的處理是c語言中做的。我們姑且猜測,c語言在什么時候去做這個映射對應呢?我提出一個想法,我們在加載SystemloadLibrary的時候會引起c庫文件中的一個觸發,在那個觸發函數里面做了對應。那么我們從文件中的結構應該是包含 觸發 實現。我們貼代碼分析:
#include <jni.h> /* /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ */ #include <stdio.h>#if 0 typedef struct {char *name; /* Java里調用的函數名 */char *signature; /* JNI字段描述符, 用來表示Java里調用的函數的參數和返回值類型 */void *fnPtr; /* C語言實現的本地函數 */ } JNINativeMethod; #endifvoid c_hello(JNIEnv *env, jobject cls) {printf("Hello, world!\n"); }static const JNINativeMethod methods[] = {{"hello", "()V", (void *)c_hello}, };/* System.loadLibrary */ 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, "JNIDemo");if (cls == NULL) {return JNI_ERR;}/* 2. map java hello <-->c c_hello */if ((*env)->RegisterNatives(env, cls, methods, 1) < 0)return JNI_ERR;return JNI_VERSION_1_4; }

我們看到,果不其然跟我們猜測的一模一樣。
的確是分為兩部分。最頂層那部分實現了c_hello函數,最下半部分實現了一個JNI_OnLoad觸發函數。哇咔咔,一切都這么順滑。那我們開墾這個觸發函數吧。
該函數功能比較單一所以寫法肯定是固定的。每次使用我們只需要去修改這么幾個點就可以了:
JNI_VERSION_1_4 這個代表的是你使用的jni的版本。

cls = (*env)->FindClass(env, "JNIDemo");

這個作用就是從env找到JNIDemo這個類,方面后面我們去做映射。故而這個JNIDemo我們做移植的時候是需要去修改的。

if ((*env)->RegisterNatives(env, cls, methods, 1) < 0)

這個地方也就是我們想要的注冊了。后面的1代表你要注冊的個數。
我們再看看這個注冊methods里面的實現。

static const JNINativeMethod methods[] = {{"hello", "()V", (void *)c_hello}, };

這個模式也是固定的
其中“hello”對應的是java中的函數名字。c_hello對應的c文件中的函數名字。前面的void * 是永遠不變的。中間的這個類型signature我是打死也不想記住怎么寫的。我們可以在寫完java文件之后使用javac JNIDemo.java進行編譯然后使用
javah -jni JNIDemo
生成一個JNIDemo.h文件

/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class JNIDemo */#ifndef _Included_JNIDemo #define _Included_JNIDemo #ifdef __cplusplus extern "C" { #endif /** Class: JNIDemo* Method: hello* Signature: ()V*/ JNIEXPORT void JNICALL Java_JNIDemo_hello(JNIEnv *, jclass);#ifdef __cplusplus } #endif #endif

我們從里面摘取

/** Class: JNIDemo* Method: hello* Signature: ()V*/

就知道這個signature 就是()V.
同時我們知道我們的c文件的hello函數書寫形式應該類似與

JNIEXPORT void JNICALL Java_JNIDemo_hello(JNIEnv *, jclass);

實際中我們的c語言函數書寫是

void c_hello(JNIEnv *env, jobject cls)

幾乎是一樣的,其中jobject 跟jclass我覺得應該是java編譯器不同導致的,我感性的認為應該是兼容的,都能用。既然我的編譯器出來的是jclass那么我的代碼就修改為

void c_hello(JNIEnv *env, jclass cls)

寫到這里,還剩下兩件事,怎樣編譯c文件為lib*.so以及java同c語言文件如何傳遞返回數據類型。我們知道java跟c的數據類型上是有區別的。包括開辟的空間大小等。最起碼的java中有string類型,然而我們的c語言中就沒有。因此,這部分還算是一個不大不小的知識點。按下不表下集再說。

總結

以上是生活随笔為你收集整理的JAVA进阶day07JNI(java调用c)A部分的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。