1. 程式人生 > >Linux下使用JNI的常見問題及解決方案

Linux下使用JNI的常見問題及解決方案

JNI是java和C/C++混合程式設計的介面,可以很方便地實現java呼叫C/C++語言。具體的使用方法,網上有很多教程,在此不做過多介紹。本部落格只關注在使用JNI的過程中的常見問題。

1.     生成標頭檔案用命令:javah*.class

     這是錯誤的。執行上述命令會提示:java.lang.IllegalArgumentException: Not a valid class name:SegNative.class錯誤。錯誤原因和利用java命令執行程式一樣,只需要指出字首即可,無需給出.class字尾。

2.     版本問題

    jdk6和jdk7中某些JNI方法稍有不同,注意轉換。例如,C中獲取字串的方法GetStringUTFChars在兩個jdk版本中就不同。老的jdk6版本使用方法為:

char* name=(char*)(*env)->GetStringUTFChars(env,Name,NULL);

而在jdk7中,方法呼叫變為:

const char* name=env->GetStringUTFChars(Name,0);
其他的版本問題及函式引數含義可以通過檢視API獲得更全面的資料。

3.     利用g++編譯原始檔找不到jni.h標頭檔案

可以在編譯時利用-I選項指定jni.h標頭檔案所在目錄:

g++ -I/usr/local/jdk1.7.0_25/include/ ……
4.     利用g++編譯原始檔找不到jni_md.h

這是因為在jni.h中引用了jni_md.h標頭檔案,該標頭檔案和jni.h不在一個目錄下,所以我們還需要再指定jni_md.h的目錄:

g++ -I/usr/local/jdk1.7.0_25/include/  -I/usr/local/jdk1.7.0_25/include/linux/……
可以看出jni_md.h放在和jni.h同級的目錄linux下。

5.     不會生成動態連結庫

生成動態連結庫,需要在編譯時宣告-shared選項:

g++ -I /usr/local/jdk1.7.0_25/include/  -I /usr/local/jdk1.7.0_25/include/linux/SegNative.cpp –shared –o lib***.so
此外,我們也無需先生成相應的.o檔案,直接指定動態連結庫的名字即可。

6.     編譯動態連結庫報錯:couldnot read symbols: Bad value

需要在編譯的時候指定選項:-fPIC

g++ -I /usr/local/jdk1.7.0_25/include/  -I /usr/local/jdk1.7.0_25/include/linux/SegNative.cpp –shared –o lib***.so -fPIC
7.     執行的時候找不到動態連結庫

這個問題主要有兩個原因:

  • 生成的動態連結庫名字不對:我們在java語言中宣告的動態連結庫如果名為A,則我們在編譯時則需要將動態連結庫的名字宣告為libA.so,否則會報錯。
  • 路徑不對,java找不到動態連結庫。java會在特定的目錄尋找動態連結庫,可以通過列印java.library.path檢視java會在哪些目錄查詢動態連結庫:
System.out.println(System.getProperty("java.library.path"));
我的電腦列印結果為:

.:/opt/intel/impi/3.2.1.009/lib/:/usr/local/cuda/lib/:/root/NVIDIA_CUDA_SDK/lib/:/root/NVIDIA_CUDA_SDK/common/lib/:/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib

我們可以看到相關lib都包括在該路徑下。特別注意一點是第一個路徑是.,這表示java會在當前路徑下尋找相關動態連結庫。因而只要我們將動態連結庫和.class檔案放在一起就不會存在找不到動態連結庫的問題。如果列印中不包括當前目錄,我們可以通過修改LD_LIBRARY_PATH指定當前目錄。

        此外,我們也可以在執行的過程中指定:

java –Djava.library.path=”/home/savedlib/”executablefile

利用這種方法,程式可以指定不在當前目錄的動態連結庫。

8.     如何返回String陣列

我們採用本地化的目的不僅僅是為了計算,我們也需要返回結果。如果返回的結果是一個String陣列,則可以仿照下面的程式碼實現:

JNIEXPORT jobjectArray JNICALL Java_Segment_segment(JNIEnv *env, jobject obj, jstring Name, jint seg_len, jint stride)
{
    const char* name=env->GetStringUTFChars(Name,0);
    int clip_num;
    name_list* head,*temp;//結構體是隻包含一個char陣列的連結串列

    CReadWav wavFile;
    head=wavFile.segment(name,seg_len,stride,&clip_num);

    //1 獲得字串的類
    jclass str_cls=env->FindClass("java/lang/String");
    if(str_cls==NULL) return NULL;
    //2 將返回結果的元素設定為String型別
    jobjectArray  result=env->NewObjectArray(clip_num,str_cls,NULL);
    if(result==NULL) return NULL;

    temp=head;
    for(int i=0;i<clip_num;i++)
    {
	//3 由char*獲得一個String
        jstring sn=env->NewStringUTF(temp->name);
	//4 將String賦值給返回結果
        env->SetObjectArrayElement(result,i,sn);
        temp=temp->next;
    }
    return result;
}

由程式碼可以看出,返回一個String陣列的方法很簡單:首先宣告返回結果為一個數組,陣列的每一個元素都是String,然後為每一個元素賦值。此外我們也要注意版本問題,在編寫的時候按照自己的jdk版本選擇相應的函式,可以通過檢視jni.h來了解相關函式的引數。

9.    讀取String陣列

假設我們需要從String陣列中讀取一系列字串,則本地函式引數有一個為:jobjectArrayNames,型別是object的陣列。在原生代碼訪問中需要首先將其轉換成jstring,然後在轉成constchar*的本地字串陣列,程式碼如下:

int file_num=env->GetArrayLength(Names);

for(int i=0;i<file_num;i++)
{
    jstring Name=(jstring)env->GetObjectArrayElement(Names,i);
    const char* name=env->GetStringUTFChars(Name,0);
}

我們可以看出,訪問字串陣列的流程很簡單:首先獲得字串陣列的長度;然後獲得每一個字串,由於返回的結果是object型別,我們需要將其強轉成jstring型別;最後再將jstring型別轉換成constchar*型別即可在本地函式中使用。

10.    返回自定義物件陣列

這可能是jni應用中最複雜的情況了。返回自定義物件就非常麻煩,加上陣列又使情況複雜化。我們用一個例項來講解如何返回自定義物件陣列。首先定義一個java類表示要返回的物件:

public class Feature
{
    private String name;
    private int length;
    private float[] data;

    public Feature()
    {
    }

    public String getName()
    {
        return this.name;
    }

    public int getLength()
    {
        return this.length;
    }

    public float[] getData()
    {
        return this.data;
    }
}

該物件有三個成員變數,一個是String,一個是int,一個是float陣列。還有一個預設的建構函式和三個get函式分別返回相關成員變數的值。接下來給出本地函式的實際操作過程:

    //1 獲得Feature類
    jclass feature_cls=env->FindClass("Feature");
    if(feature_cls==NULL) return NULL;

    //2 建立一個元素型別為Feature的陣列
    jobjectArray  result=env->NewObjectArray(file_num,feature_cls,NULL);
    if(result==NULL) return NULL;

    //3 獲得Feature類的預設建構函式
    jmethodID constructor=env->GetMethodID(feature_cls,"<init>","()V");
    if(constructor==NULL) return NULL;

    //4 獲得Feature的三個成員變數
    jfieldID nameid=env->GetFieldID(feature_cls,"name","Ljava/lang/String;"); 
    if(nameid==NULL) return NULL;
    jfieldID lengthid=env->GetFieldID(feature_cls,"length","I");//int
    if(lengthid==NULL) return NULL;
    jfieldID dataid=env->GetFieldID(feature_cls,"data","[F");//float array
    if(dataid==NULL) return NULL;

    for(int i=0;i<file_num;i++)
    {
        //5 建立一個新的Feature物件
        jobject feature_obj=env->NewObject(feature_cls,constructor);

        //6 設定物件的成員變數值
        env->SetObjectField(feature_obj,nameid,env->NewStringUTF(head->name));
        env->SetIntField(feature_obj,lengthid,head->length);
        jfloatArray floatarray=env->NewFloatArray(head->length);
        env->SetFloatArrayRegion(floatarray,0,head->length,head->data);
        env->SetObjectField(feature_obj,dataid,floatarray);

	    //7 為陣列的每個元素賦值
        env->SetObjectArrayElement(result,i,feature_obj);
        
    }

其實關鍵操作還是在自定義物件的建立和賦值上,陣列的操作和之前類似。為了建立一個自定義物件,我們需要利用該物件的建構函式,因為建構函式的目的即是建立一個新物件。為了給物件的成員變數賦值,我們需要獲得這些成員變數,然後根據其型別賦值。更詳細的解釋可參考:http://dolphin-ygj.iteye.com/blog/519489





相關推薦

dotnet core Linux圖片驗證碼解決方案

https tps 3.0 驗證碼 code 地址 hub 圖片 dot 方案來源:https://github.com/zkweb-framework/zkweb.system.drawing 百度大多都是安裝libgdiplus方法 不說了 然後驗證碼亂碼問題在上面地址

linuxarp攻擊的解決方案[原]

            最近我們這棟宿舍樓的ARP病毒又爆發了,Windows下的使用者可以使用antiArp防火牆, 基本上能解決問題,可是喜歡linux的兄弟姐妹們怎麼辦呢,我今天就遇到這個檔子煩心事。我用的是fedora core 6,我先把幾個映象檔案掛上,用關鍵字 arp 一搜,就搜到了arptab

Linux 軟體安裝常見問題解決方案

linux下安裝軟體出現的問題解決方案,持續更新中 1、Redis安裝問題 Redis版本:4.0.6 Linux版本:CentOS 7 場景:編譯Redis時 提示 /bin/sh: cc:

AES加密解密Windowslinux結果不同的解決方案

現象描述: 在 windows 作業系統下加解密正常,但部署到 linux 環境中相同的輸入加密結果不正確,並且每次執行返回的結果都不同。 原理: 加密過程需手動指定隨機數的生成規則,同理在解密過程中也需手動指定隨機數的生成規則。 java.security.Secure

onchange事件在IE和FF的表現解決方案

在最近做的一個專案中,有這麼一個功能點:頁面上有一個checkbox,當用戶選擇或者取消選擇該checkbox時會向後臺發一個jsonp請求。當時的實現是為這個checkbox新增一個onchange事件,但結果卻出人意料,為此,我深入的研究了一下,發現了onchange事

Linux使用JNI常見問題解決方案

JNI是java和C/C++混合程式設計的介面,可以很方便地實現java呼叫C/C++語言。具體的使用方法,網上有很多教程,在此不做過多介紹。本部落格只關注在使用JNI的過程中的常見問題。 1.     生成標頭檔案用命令:javah*.class      這是錯誤的。執

Linux安裝Nginx完整教程常見錯誤解決方案

1.Nginx安裝環境 Nginx是C語言開發,建議在linux上執行,本教程使用Centos7.0作為安裝環境.1)gcc安裝nginx需要先將官網下載的原始碼進行編譯,編譯依賴gcc環境,如果沒有

linuxssh連接慢的原因調查解決方案

acc 設置 發現 lin opened 左右 config 連接 war 項目中的一臺阿裏雲,最近一段時間出現ssh的時候,連接非常慢,大概輸入密碼後要10-20秒左右才能連上,以下記錄調查過程及解決辦法 通過網上的一些查詢,發現大都是因為設置dns,hosts或者通過關

Azure Linux 虛機上配置 RAID 的常見問題解決方案

簡介 獨立硬碟冗餘陣列(RAID, Redundant Array of Independent Disks),簡稱磁碟陣列。能增強資料整合度,增強容錯功能,增加處理量或容量。詳情參見這篇文章。 配置方法 Note 以下範例均在 CentOS 平臺執行,其他版本

Linux PHP安裝遇見的問題解決方案

linux php安裝出現錯誤 解決方法 遇到的問題與解決方案 問題一:報以下錯誤./configure以下錯誤發生Sorry, I cannot run apxs. Possiblereasons follow:1. Perl is not installed2. apxs was not fou

移動端常見問題解決方案

ont 方案 不同 頁面 initial sca 刷新 detect 編碼 H5頁面窗口自動調整到設備寬度,並禁止用戶縮放頁面 <meta name="viewport" content="width=device-width,initial-scale=1.0,

asp.net MVC 常見安全問題解決方案

container coo baidu his ring article 調試工具 並且 part asp.net MVC 常見安全問題及解決方案一.CSRF (Cross-site request forgery跨站請求偽造,也被稱為“one click attack”或

CentOS7Tomcat啟動慢的原因解決方案

按鍵 source 命令 mon 進程 tomcat apps 守護進程 中斷 現象   在一次CentOS 7系統中安裝Tomcat,啟動過程很慢,需要幾分鐘,經過查看日誌,發現耗時在這裏:是session引起的隨機數問題導致的。Tocmat的Session ID是通過S

Ubuntu常見問題解決方案

簡單的 com das == 終端 隨筆 alt+ http nbsp 萬事開頭難,都不知道從哪裏寫起,就從最簡單的開始寫好了。這篇隨筆主要羅列出在使用Ubuntu中經常會遇到的問題並附上解決辦法,根據篇幅長短再決定要不要分為數個系列。 1.  剛進去Ubuntu圖形界面,

Java常見的幾種內存溢出解決方案

-xmx 系列 lba pan fff 特征 聚類算法 聲明 space 1.JVM Heap(堆)溢出:java.lang.OutOfMemoryError: Java heap space JVM在啟動的時候會自動設置JVM Heap的值, 可以利用JVM提

android gradle打包常見問題解決方案

opener imu use dex 虛擬機 解決 sco expected tac 背景: 問題: Q1: UNEXPECTED TOP-LEVEL ERROR: java.lang.OutOfMemoryError: Java heap space at com.

常見的兼容性問題解決方案

city alpha cit osi 解決 isp 解決方案 ive pac 1、雙邊距BUG :塊級元素float後再設置margin時,IE6顯示的margin比設置的大 使用 _display: inline 解決 2、3像素問題:一個元素左

VMware網絡模式配置與IP地址無法正常獲取解決方案

什麽 應該 任務 互連 但我 intern 資料 for 互聯 目錄一 網絡配置中出現的錯誤及解決方案二 VMware下網絡配置的三種模式簡介 1、橋接模式(Bridged) 2、網絡地址轉化模式(NAT) 3、僅主機模式(host-only)一 網

LoadRunner 常見錯誤收集解決方案

上一個 exc nic win iss 溢出 red sage sed 一. This Vuser already started a transaction with the same name, and has not yet processed the corresp

RHEL7Tomcat啟動慢的原因解決方案

tomcat分析結果 主要原因是生成隨機數的時候卡住了,導致tomcat啟動不了。是否有足夠的熵來用於產生隨機數,可以通過如下命令來查看[root@tomcat tools]# cat /proc/sys/kernel/random/entropy_avail7為了加速/dev/random提供隨機數的速度,