熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> JSP教程 >> 正文

如何在Linux平台下使用JNI提高Java效率

2013-11-15 09:48:03  來源: JSP教程 

  Java的出現給大家開發帶來的極大的方便但是如果我們有大量原有的經過廣泛測試的非Java代碼將它們全部用Java來重寫恐怕會帶來巨大的工作量和長期的測試如果我們的應用中需要訪問到特定的設備甚至是僅符合公司內部信息交互規范的設備或某個特定的操作系統才有的特性Java就顯得有些力不從心了面對這些問題Sun公司在JDK中就定義了JNI規范它規定了Java應用程序對本地方法的調用規則
  
  實現步驟及相關函數使用
  
  本文將一步步說明在Linux平台下如何實現本地共享庫與Java協同工作Hello World程序是目前標准的入門第一步那麼我也以類似的應用最為樣例
  
  第一步定義一個 Java 類 Hello 它提供SayHello方法
  
  此時應注意兩點
  
  為要使用的每個本地方法編寫本地方法聲明其聲明方式與普通Java方法接口沒什麼不同只是必須指定 native 關鍵字如下所示
  
  public native void SayHello(String strName);
  
  在這個函數中我們將根據傳進的人名向某人問好
  
  必須顯式地加載本地代碼庫我們需在類的一個靜態塊中加載這個庫
  
  static
  {
  SystemloadLibrary(hello);
  }
  
  再加上必要的異常處理就生成如下源文件Hellojava
  
  public class Hello
  {
  static
  {
  try
  {
  //此處即為本地方法所在鏈接庫名
  SystemloadLibrary(hello);
  }
  catch(UnsatisfiedLinkError e)
  {
  Systemerrprintln
  ( Cannot load hello library:\n +
  etoString() );
  }
  }
  public Hello()
  {
  }
  //聲明的本地方法
  public native void SayHello(String strName);
  }
  
  編譯後生成Helloclass文件
  
  第二步生成本地鏈接庫具體過程如下
  
  要為以上定義的類生成 Java 本地接口頭文件需使用javahJava 編譯器的 javah 功能將根據Hello類生成必要的聲明此命令將生成Helloh文件我們在共享庫的代碼中要包含它javah不使默認內部命令需要指明路徑它在JDK的bin目錄下在我的Linux環境下命令如下
  
  /home/jbuilder/jdk/bin/javah Hello
  
  生成的Helloh 文件 內容如下所示
  
  /* DO NOT EDIT THIS FILE
  it is machine generated */
  #include
  /* Header for class Hello */
  #ifndef _Included_Hello
  #define _Included_Hello
  #ifdef __cplusplus
  extern C {
  #endif
  /*
  * Class:   Hello
  * Method:  SayHello
  * Signature: (Ljava/lang/String;)V
  */
  JNIEXPORT void JNICALL Java_Hello_SayHello
  (JNIEnv * jobject jstring);
  #ifdef __cplusplus
  }
  #endif
  #endif
  
  在與Helloh相同的路徑下創建一個CPP文件Hellocpp內容如下
  
  #include Helloh
  #include
  //與Helloh中函數聲明相同
  JNIEXPORT void JNICALL Java_Hello_SayHello
  (JNIEnv * env jobject arg jstring instring)
  {
  //從instring字符串取得指向字符串UTF編碼的指針
  const jbyte *str =
  (const jbyte *)env>GetStringUTFChars
  ( instring JNI_FALSE );
  printf(Hello%s\nstr);
  //通知虛擬機本地代碼不再需要通過str訪問Java字符串
  env>ReleaseStringUTFChars
  ( instring (const char *)str );
  return;
  }
  
  所有的JNI調用都使用了JNIEnv*類型的指針習慣上在CPP文件中將這個變量定義為evn它是任意一個本地方法的第一個參數env指針指向一個函數指針表在VC中可以直接用>操作符訪問其中的函數
  
  jobject 指向在此 Java 代碼中實例化的 Java 對象 LocalFunction的一個句柄相當於this指針後續的參數就是本地調用中有Java程序傳進的參數本例中只有一個String型參數對於字符串型參數因為在本地代碼中不能直接讀取Java字符串而必須將其轉換為C/C++字符串或Unicode以下是三個我們經常會用到的字符串類型處理的函數
  
 educitycn/img_///gif >

  
educitycn/img_///gif >

  參數array 數組對象
  
  isCopy 如果進行拷貝指向以JNI_TRUE填充的jboolean否則指向以JNI_FALSE填充的jboolean
  
  例如 jboolean * GetBooleanArrayElements(jbooleanArray array jboolean *isCopy)
  
  void ReleaseXxxArrayElements(xxxArray arrayxxx *elems jint mode)
  
  通知虛擬機不再需要從GetXxxArrayElements得到的指針
  
  參數array 數組對象
  
  elems 不再需要的指向數組元素的指針
  
  mode =在更新數組元素後釋放elems緩沖器
  
  JNI_COMMIT=在更新數組元素後不釋放elems緩沖器
  
  JNI_ABORT=不更新數組元素釋放elems緩沖器
  
  例如void ReleaseBooleanArrayElements(jbooleanArray arrayjboolean *elems jint mode)
  
  xxxArray NewXxxArray(jsize len)
  
  產生一個新的數組通常在反值類型為數組型時用到
  
  參數len 數組中元素的個數
  
  例如jbooleanArray NewBooleanArray(jsize len)
  
  編譯生成共享庫
  
  使用GCC時必須通知編譯器在何處查找此Java本地方法的支持文件並且顯式通知編譯器生成位置無關的代碼在我的環境中按如下過程編譯
  
  gccI/home/jbuilder/jdk/includeI/home
  /jbuilder/jdk/include/linux fPIC c Helloc
  
  生成Helloo
  
  gcc shared Wlsonamelibhelloso
  o libhelloso Helloo
  
  生成libhelloso
  
  接下來將生成的共享庫拷貝為標准文件名
  
  cp libhelloso libhelloso
  
  最後通知動態鏈接程序此共享文件的路徑
  
  export LD_LIBRARY_PATH=`pwd`
  :$LD_LIBRARY_PATH
  
  編寫一個簡單的Java程序來測試我們的本地方法
  
  將如下源碼存為ToSayjava
  
  import Hello;
  import javautil*;
  public class ToSay
  {
  public static void main(String argv[])
  {
  ToSay say = new ToSay();
  }
  public ToSay()
  {
  Hello h = new Hello();
  //調用本地方法向John問好
  hSayHello(John);
  }
  }
  
  用javac編譯ToSayjava生成ToSayclass向執行普通Java程序一樣使用java ToSay我們會看到在屏幕上出現HelloJohn到這裡我們就將整個的本地調用編寫過程大概浏覽了一遍
  
  應用中注意事項
  
  .如果可以通過TCP/IP實現Java代碼與本地C/C++代碼的交互工作那麼最好不使用以上提到的JNI的方式因為一次JNI調用非常耗時大概要花個毫秒
  
  .在一個Applet應用中不要使用JNI因為在 applet 中可能引發安全異常
  
  .將所有本地方法都封裝在單個類中這個類調用單個 DLL對於每種目標操作系統都可以用特定於適當平台的版本替換這個 DLL這樣就可以將本地代碼的影響減至最小並有助於將以後所需的移植問題包含在內
  
  .本地方法要簡單盡量將生成的DLL 對任何第三方運行時 DLL 的依賴減到最小使本地方法盡量獨立以將加載DLL 和應用程序所需的開銷減到最小如果必須要運行時 DLL則應隨應用程序一起提供它們
  
  .本地代碼運行時沒有有效地防數組越界錯誤錯誤指針引用帶來的間接錯誤等所以必須保證保證本地代碼的穩定性因為絲毫的錯誤都可能導致Java虛擬機崩潰
  
  結束語
  
  JNI調用規范給我們復用原有其它語言的代碼提供了簡單易用的接口可以節省大量的財力使我們可以在享受Java帶來的開發速度的同時不必放棄舊有資源
  
  const char* GetStringUTFChars
  (jstring stringjboolean* isCopy)
  
  返回指向字符串UTF編碼的指針如果不能創建這個字符數組返回null這個指針在調用ReleaseStringUTFChar()函數之前一直有效
  
  參數string Java字符串對象
  
  isCopy 如果進行拷貝指向以JNI_TRUE填充的jboolean否則指向以JNI_FALSE填充的jboolean
  
  void ReleaseStringUTFChars(jstring str const char* chars)
  
  通知虛擬機本地代碼不再需要通過chars訪問Java字符串
  
  參數string Java字符串對象
  
  chars 由GetStringChars返回的指針
  
  jstring NewStringUTF(const char *utf)
  
  返回一個新的Java字符串並將utf內容拷貝入新串如果不能創建字符串對象
  
  返回null通常在反值類型為string型時用到
  
  參數utf UTF編碼的字符
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19137.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.