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方法 不說了 然後驗證碼亂碼問題在上面地址
linux下arp攻擊的解決方案[原]
最近我們這棟宿舍樓的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加密解密Windows下跟linux下結果不同的解決方案
現象描述: 在 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環境,如果沒有
linux下ssh連接慢的原因調查及解決方案
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”或
CentOS7下Tomcat啟動慢的原因及解決方案
按鍵 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
RHEL7下Tomcat啟動慢的原因及解決方案
tomcat分析結果 主要原因是生成隨機數的時候卡住了,導致tomcat啟動不了。是否有足夠的熵來用於產生隨機數,可以通過如下命令來查看[root@tomcat tools]# cat /proc/sys/kernel/random/entropy_avail7為了加速/dev/random提供隨機數的速度,