亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Step By Step_Java通過JNI調(diào)C程序執(zhí)行

mist14 / 722人閱讀

摘要:以下將以一個(gè)實(shí)際例子展示通過調(diào)用打印主要記錄實(shí)現(xiàn)的過程和方法,對(duì)其中的一些原理和規(guī)范不做具體展開。指向在此代碼中實(shí)例化的對(duì)象的一個(gè)句柄,相當(dāng)于指針。加載本地共享庫運(yùn)行結(jié)果如下傳遞參數(shù)接下來看一下如何通過向傳遞參數(shù)。

文章為本人編纂,轉(zhuǎn)載請(qǐng)聯(lián)系作者并注明出處。

在日常項(xiàng)目中,我們可能會(huì)遇到需要用Java去命令行執(zhí)行命令或執(zhí)行shell腳本的情況,但有時(shí)可能又會(huì)因?yàn)槟承┉h(huán)境或者權(quán)限等無法排查的原因調(diào)用失敗,這時(shí)候就可以通過一個(gè)中間介質(zhì)C來執(zhí)行。尤其是在對(duì)某些項(xiàng)目代碼(已經(jīng)過廣泛測試或需要訪問特定設(shè)備)進(jìn)行重寫,Java恐怕有些力不從心,而Sun公司定義的JNI規(guī)范,規(guī)定了Java對(duì)本地方法的調(diào)用規(guī)則,這就大可不必廢棄舊有代碼。

以下將以一個(gè)實(shí)際例子展示Java通過JNI調(diào)用C打印“Hello World!”主要記錄實(shí)現(xiàn)的過程和方法,對(duì)其中的一些原理和規(guī)范不做具體展開。想深入了解的可以參考Oracle的官方文檔,貼上地址:
JNI Interface Functions and Pointers

環(huán)境介紹
操作系統(tǒng):Ubuntu Gnome 16.04 LTS

Java:Java 1.8.0_111

C:gcc version 5.4.0

實(shí)現(xiàn)步驟 Hello World
1、定義一個(gè)Java類——JavaCallC.java

首先定義一個(gè)Java類JavaCallC.java,在類中實(shí)現(xiàn)一個(gè)SayHello方法,并用關(guān)鍵字native為本地方法編寫本地聲明;

public native void SayHello();

然后在類中的靜態(tài)代碼塊顯示地加載本地代碼庫;

static {
    System.loadLibrary("hello"); //加載本地共享庫
}

再加上main方法和一些必要的異常處理程序,就生成以下源文件(當(dāng)然,也可以將本地方法放在另外一個(gè)多帶帶的類中)。

package com.jni.c;

public class JavaCallC {
    
    /**
     * java通過JNI調(diào)用C
     * @author xiaosong 2017-04-03
     */
    public static void main(String[] args) {
        JavaCallC call = new JavaCallC();
        call.SayHello();
    }    
    
    /**
     * 加載共享庫的本地方法
     */
    public native void SayHello();
    
    static {
        try {
            System.loadLibrary("hello"); //加載本地共享庫
        }catch(UnsatisfiedLinkError e) {
            System.err.println("無法加載共享庫:" + e.toString());
        }
    }

}
2、生成 Java 本地接口頭文件

P.S. 如果沒有使用IDE的,需先用 javac 將類編譯為 .class 文件。
要為以上定義的類生成 Java 本地接口頭文件,需使用 javah,Java 編譯器的 javah 功能將根據(jù) JavaCallC 類生成必要的聲明,此命令將生成一個(gè) .h 后綴的頭文件,我們?cè)诠蚕韼斓拇a中要包含它。在工程項(xiàng)目的編譯文件 bin 目錄(也可能是build)下執(zhí)行如下命令():

javah -jni [package.class]

執(zhí)行命令后生成了一個(gè) com_jni_c_JavaCallC.h 頭文件,頭文件的內(nèi)容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class com_jni_c_JavaCallC */

#ifndef _Included_com_jni_c_JavaCallC
#define _Included_com_jni_c_JavaCallC
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_jni_c_JavaCallC
 * Method:    SayHello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_jni_c_JavaCallC_SayHello
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
3、創(chuàng)建共享庫C文件

在與 com_jni_c_JavaCallC.h 相同的路徑下創(chuàng)建一個(gè) .c 文件 hello.c ,在C文件中引入該頭文件 ,并使用和頭文件中一致的方法來聲明函數(shù)。內(nèi)容如下:

記得要為 JNIEnv * 指針和 jobject 對(duì)象定義變量,習(xí)慣上將這兩個(gè)變量定義為 envobj 。
env 指針是任意一個(gè)本地方法的第一個(gè)參數(shù),它指向一個(gè)函數(shù)指針表。jobject 指向在此 Java 代碼中實(shí)例化的 Java 對(duì)象 LocalFunction 的一個(gè)句柄,相當(dāng)于 this 指針。

#include "com_jni_c_JavaCallC.h"
#include 

JNIEXPORT void JNICALL Java_com_jni_c_JavaCallC_SayHello
(JNIEnv * env, jobject obj) {

    printf("Hello World! 
");

    return;
}
4、編譯生成共享庫文件

編譯文件時(shí),需要告知 GCC 編譯器在何處查找Java本地方法的支持文件 jni.hjni_md.h ,這兩個(gè)文件一般是在 ../jdk/include../jdk/include/linux 兩個(gè)目錄下(AIX在 ../jdk/include/aix 目錄;Windows在 ../jdk/include/win32 目錄),在我的環(huán)境中按如下過程編譯。

先生成 hello.o

gcc -fPIC -I/usr/lib/jdk1.8.0_111/include -I/usr/lib/jdk1.8.0_111/include/linux -c hello.c

再生成 libhello.so
(共享庫 .so 的文件名必須是 lib+文件名

gcc -shared hello.o -o libhello.so

拷貝 libhello.so 到共享庫目錄:
(共享庫目錄一般為 ../jre/lib/amd64/server

sudo cp libhello.so /usr/lib/jdk1.8.0_111/jre/lib/amd64/server

5、運(yùn)行Java程序

由于我未配置 $LD_LIBRARY_PATH 環(huán)境變量,所以程序無法加載到共享庫 hello ,因而我改寫成通過全路徑的方式來加載共享庫。

//System.loadLibrary("hello"); //加載本地共享庫
System.load("/usr/lib/jdk1.8.0_111/jre/lib/amd64/server/libhello.so");

運(yùn)行結(jié)果如下:

傳遞參數(shù)

接下來看一下Java如何通過JNI向C傳遞參數(shù)。本文中僅以 String 字符串為例,其他類型的參數(shù)的處理可參考文首提供的Oracle官方文檔,方法大體上是一致的。

1、定義本地方法參數(shù)

先在聲明的本地方法中定義參數(shù):

public native void SayHello(String strName1, String strName2);

然后在 main 方法中調(diào)用它并傳遞參數(shù):

public static void main(String[] args) {
    JavaCallC call = new JavaCallC();
    call.SayHello("Info", "Xiaosong");
}
2、編譯并生成頭文件

生成頭文件的方法同上,這時(shí)候看一下生成的頭文件有何區(qū)別。

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class com_jni_c_JavaCallC */

#ifndef _Included_com_jni_c_JavaCallC
#define _Included_com_jni_c_JavaCallC
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_jni_c_JavaCallC
 * Method:    SayHello
 * Signature: (Ljava/lang/String;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_jni_c_JavaCallC_SayHello
  (JNIEnv *, jobject, jstring, jstring);

#ifdef __cplusplus
}
#endif
#endif

可以看到函數(shù)聲明里多了兩個(gè) jstring ,這就對(duì)應(yīng)于我們要傳遞的兩個(gè) String 參數(shù)。
其他數(shù)值型參數(shù)和數(shù)組型參數(shù)對(duì)照如下:

3、創(chuàng)建共享庫C文件

同樣地,在編寫 hello.c 文件時(shí),我們需要為傳遞的兩個(gè)參數(shù)定義變量;

JNIEXPORT void JNICALL Java_com_jni_c_JavaCallC_SayHello
(JNIEnv * env, jobject obj, jstring instring1, jstring instring2) 

對(duì)于字符串型參數(shù),因?yàn)樵诒镜卮a中不能直接讀取 Java 字符串,而必須將其轉(zhuǎn)換為 C /C++ 字符串或 Unicode。此處C的寫法和C++的寫法略微不同;

/**
 * C 寫法
 */
//從instring字符串取得指向字符串UTF編碼的指針;
const char *info = (*env)->GetStringUTFChars(env, instring1, 0);
/**
 * C++ 寫法
 */
const char *info = env->GetStringUTFChars(instring1, 0);

//通知虛擬機(jī)本地代碼不再需要通過 info 訪問Java字符串;

/**
 * C 寫法
 */
(*env)->ReleaseStringUTFChars(env, instring1, info);
/**
 * C++ 寫法
 */
env->ReleaseStringUTFChars(instring1, info);

再加上一些簡單的異常處理,完整的含參的 hello.c 如下:

#include "com_jni_c_JavaCallC.h"
#include 
#include 


JNIEXPORT void JNICALL Java_com_jni_c_JavaCallC_SayHello
(JNIEnv * env, jobject arg, jstring instring1, jstring instring2) {

    //從instring字符串取得指向字符串UTF編碼的指針
    const char *info = (*env)->GetStringUTFChars(env, instring1, 0);
    const char *name = (*env)->GetStringUTFChars(env, instring2, 0);

    if (strlen(info)==0 || strlen(name)==0)
    {
        printf("參數(shù)缺失!
");
    }else {
        printf("%s : Hello %s 
", info, name);
    };

    //通知虛擬機(jī)本地代碼不再需要通過str訪問java字符串
    (*env)->ReleaseStringUTFChars(env, s1, str);
    (*env)->ReleaseStringUTFChars(env, s2, user);

    return;
}
4、編譯生成共享庫文件

方法和操作同上

5、運(yùn)行Java程序

以下是調(diào)用 call.SayHello("Information", "Xiaosong"); 執(zhí)行的結(jié)果:

以下是調(diào)用 call.SayHello("Information", ""); 執(zhí)行的結(jié)果:

至此,Java通過JNI調(diào)C的例子全部結(jié)束,當(dāng)中如有什么不足或錯(cuò)誤還請(qǐng)指正。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/66945.html

相關(guān)文章

  • Java 外部函數(shù)接口:JNI, JNA, JNR

    摘要:我們知道,發(fā)起函數(shù)調(diào)用,需要構(gòu)造一個(gè)棧幀。構(gòu)造棧幀的具體實(shí)現(xiàn)細(xì)節(jié)的選擇,被稱為調(diào)用慣例。要想完成這個(gè)函數(shù)調(diào)用邏輯,就要運(yùn)行時(shí)構(gòu)造棧幀,生成參數(shù)壓棧和清理堆棧的工作。目前,幾乎支持全部常見的架構(gòu)。 原文:http://nullwy.me/2018/01/java...如果覺得我的文章對(duì)你有用,請(qǐng)隨意贊賞 遇到的問題 前段時(shí)間開發(fā)的時(shí)候,遇到一個(gè)問題,就是如何用 Java 實(shí)現(xiàn) chdir...

    pubdreamcc 評(píng)論0 收藏0
  • JAVA運(yùn)行時(shí)簡述(HotSpot)

    摘要:拆解虛擬機(jī)的基本步聚如下首先,要等待到自身成為唯一一個(gè)正在運(yùn)行的非守護(hù)線程時(shí),在整個(gè)等待過程中,虛擬機(jī)仍舊是可工作的。將相應(yīng)的事件發(fā)送給,禁用,并終止信號(hào)線程。 本文簡單介紹HotSpot虛擬機(jī)運(yùn)行時(shí)子系統(tǒng),內(nèi)容來自不同的版本,因此可能會(huì)與最新版本之間(當(dāng)前為JDK12)存在一些誤差。 1.命令行參數(shù)處理HotSpot虛擬機(jī)中有大量的可影響性能的命令行屬性,可根據(jù)他們的消費(fèi)者進(jìn)行簡...

    hosition 評(píng)論0 收藏0
  • 細(xì)說JS異步發(fā)展歷程

    摘要:換句話說,當(dāng)一個(gè)異步過程調(diào)用發(fā)出后,調(diào)用者不會(huì)立刻得到結(jié)果。參考文章珠峰架構(gòu)課墻裂推薦細(xì)說異步函數(shù)發(fā)展歷程異步編程謝謝各位小伙伴愿意花費(fèi)寶貴的時(shí)間閱讀本文,如果本文給了您一點(diǎn)幫助或者是啟發(fā),請(qǐng)不要吝嗇你的贊和,您的肯定是我前進(jìn)的最大動(dòng)力。知其然知其所以然,首先了解三個(gè)概念: 1.什么是同步? 所謂同步,就是在發(fā)出一個(gè)調(diào)用時(shí),在沒有得到結(jié)果之前,該調(diào)用就不返回。但是一旦調(diào)用返回,就得到返回值了...

    Alfred 評(píng)論0 收藏0
  • 細(xì)說JS異步發(fā)展歷程

    摘要:參考文章珠峰架構(gòu)課墻裂推薦細(xì)說異步函數(shù)發(fā)展歷程異步編程謝謝各位小伙伴愿意花費(fèi)寶貴的時(shí)間閱讀本文,如果本文給了您一點(diǎn)幫助或者是啟發(fā),請(qǐng)不要吝嗇你的贊和,您的肯定是我前進(jìn)的最大動(dòng)力。 知其然知其所以然,首先了解三個(gè)概念: 1.什么是同步? 所謂同步,就是在發(fā)出一個(gè)調(diào)用時(shí),在沒有得到結(jié)果之前,該調(diào)用就不返回。但是一旦調(diào)用返回,就得到返回值了。換句話說,就是由調(diào)用者主動(dòng)等待這個(gè)調(diào)用的結(jié)果。此調(diào)...

    RiverLi 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<