2009-11-03 91 views
1

大多數詳細介紹JNI入門的文檔都介紹瞭如何使用X-Code構建新的JNI應用程序。任何人都可以鏈接到我介紹如何使用JNI與Objective-C在現有應用程序中進行交互。用JNI包裝現有應用程序

回答

1

注意:我已經完全重新從頭開始寫這個答案,現在我知道它確實有效;-)。使用Rococoa而不是JNI。

這是一個簡短的示例,我能夠顯示圖片獲取者對話框(根據您對Stephen C的回答發表的評論)。

/*** 
* INCOMPLETE: Doesn't have imports or anything like that. 
***/ 

public interface Quartz extends Library 
{ 
    public static Quartz instance = (Quartz)Native.loadLibrary("Quartz", Quartz.class); 
} 

public interface IKPictureTaker extends NSObject 
{ 
    public static final _Class CLASS = Rococoa.createClass("IKPictureTaker", _Class.class); 

    public interface _Class extends NSClass 
    { 
    /** 
    * Returns a shared {@code IKPictureTaker} instance, creating it if necessary. 
    * @return an {@code IKPictureTaker} object. 
    */ 
    IKPictureTaker pictureTaker(); 
    } 

    NSInteger runModal(); 
} 

public class IKPictureTakerTest extends JFrame 
{ 
    public static void main(String[] args) throws Exception 
    { 
    // You need a GUI before this will work. 
    new IKPictureTakerTest().setVisible(true); 

    NSAutoreleasePool pool = NSAutoreleasePool.new_(); 

    // Initialize the Quartz framework. 
    Quartz.instance.toString(); 

    // Display the dialog. 
    IKPictureTaker pictureTaker = IKPictureTaker.CLASS.pictureTaker(); 
    NSInteger result = pictureTaker.runModal(); 

    if (result.intValue() == 0) // NSCancelButton 
    { 
     System.out.println("User cancelled."); 
    } 
    else 
    { 
     assert result.intValue() == 1; // NSOKButton 
     System.out.println("User chose an image."); 
    } 

    System.out.println(pictureTaker.inputImage()); // null if the user cancelled 

    pool.release(); 
    } 
} 

如果迷路了,請嘗試Rococoa mailing lists。開發人員非常有幫助。

+0

非常感謝您的建議,我實際上一直在與Rococoa合作,因爲我無法獲得JNI,我想出了一些與您的示例非常接近的東西,但是每次運行時都會出現此錯誤: 線程異常「main」java.lang.UnsatisfiedLinkError:無法加載庫'rococoa':dlopen(librococoa.dylib,9):image not found 看來,雖然我把所有的rococoa jar和librococoa.dylib放在了位置java.library.path指出它仍然不開心。我相信我需要設置jna.library.path變量,但我找不到如何執行此操作。 – Mike2012

+3

正確,你需要使用'-Djna.library.path = <目錄,其中包含librococoa.dylib>' –

+0

哈哈我被推遲了。謝謝。 – Mike2012

1

您仍然需要編寫某種類型的JNI庫來包裝對現有代碼(又名,共享對象,DLL,服務程序等)的訪問。這是因爲JNI需要對調用的本機函數進行一種相當鈍的(但是合理的)命名約定,因爲您需要將數據移入和移出Java內存空間,並且因爲您需要在Java和本機函數之間具有概念性的「橋接」代碼。

例如,我編寫了一個JNI庫來提供對iSeries上現有C函數的訪問。一個這樣的功能從數據區讀取看起來如下:

JNIEXPORT void JNICALL Java_com_xxx_jni400_DataArea_jniGetDataArea(JNIEnv *jep, jobject thsObj, jbyteArray qulnam, jint str, jint len, jbyteArray rtndta, jint rtnlen) { 
    jbyte   *qn,*rd; 
    Qwc_Rdtaa_Data_Returned_t *drt; 
    QFBK2_T   fbk; 
    byte   nam[11],lib[11]; 
    byte   *ptr; 

    // SETUP 
    thsObj=thsObj; 
    qn=(*jep)->GetByteArrayElements(jep,qulnam,0); 
    rd=(*jep)->GetByteArrayElements(jep,rtndta,0); 
    fbk.pro=sizeof(fbk); fbk.avl=0; 

    // INVOKE 
    QWCRDTAA(rd,rtnlen,(byte*)qn,str,len,&fbk); 

    // HANDLE SUCCESSFUL INVOCATION 
    if(fbk.avl==0) { 
     drt=(Qwc_Rdtaa_Data_Returned_t*)rd; 
     if(drt->Length_Value_Returned>0) { /* pad with spaces until the length requested */ 
      ptr=(byte*)(rd+sizeof(*drt)+drt->Length_Value_Returned); 
      for(; drt->Length_Value_Returned<len; drt->Length_Value_Returned++,ptr++) { *ptr=' '; } 
      } 
     } 

    // RELEASE JAVA MEMORY LOCKS 
    (*jep)->ReleaseByteArrayElements(jep,qulnam,qn,JNI_ABORT); /* discard array changes */ 
    (*jep)->ReleaseByteArrayElements(jep,rtndta,rd,0  ); /* copy back changes */ 

    // TRANSFORM NATIVE ERROR INTO AN EXCEPTION AND THROW 
    if(fbk.avl!=0) { 
     byte    eid[8],dta[201]; 
     word    dtalen; 

     f2s(nam,sizeof(nam),(byte*)qn  ,10); 
     f2s(lib,sizeof(lib),(byte*)(qn+10),10); 

     dtalen=(word)mMin(sizeof(fbk.dta),(fbk.avl-(sizeof(fbk)-sizeof(fbk.dta)))); 
     f2s(eid,sizeof(eid),fbk.eid,sizeof(fbk.eid)); 
     f2s(dta,sizeof(dta),fbk.dta,dtalen); 
     if(mStrEquI(eid,"CPF1015") || mStrEquI(eid,"CPF1021")) { 
      throwEscape(jep,90301,"Could not find data area %s in library %s",nam,lib); 
      } 
     else if(mStrEquI(eid,"CPF1016") || mStrEquI(eid,"CPF1022")) { 
      throwEscape(jep,90301,"Not authorized to data area %s in library %s",nam,lib); 
      } 
     else if(mStrEquI(eid,"CPF1063") || mStrEquI(eid,"CPF1067")) { 
      throwEscape(jep,90301,"Cannot allocate data area %s in library %s",nam,lib); 
      } 
     else if(mStrEquI(eid,"CPF1088") || mStrEquI(eid,"CPF1089")) { 
      throwEscape(jep,90301,"Substring %i,%i for data area %s in library %s are not valid",str,len,nam,lib); 
      } 
     else { 
      if(strlen(dta)>0) { throwEscape(jep,90001,"System API QWCRDTAA returned error message ID %s (%s)",eid,dta);} 
      else    { throwEscape(jep,90001,"System API QWCRDTAA returned error message ID %s",eid);   } 
      } 
     } 
    } 

注意一個行調用現有API底層,QWCRDTAA,這是由IBM提供的;其餘的是以Java爲中心的包裝,這是打電話和處理結果所必需的。另外,要非常小心您所調用的是線程安全的,或者您在Java層中全局保護代碼不受全局併發調用的影響,或者您在O/S層中使用互斥鎖保護代碼。注意:非線程安全的本機代碼全局爲非線程安全;您必須防止與所有其他非線程安全的本機代碼並行調用,而不僅僅是您調用的一種方法。這是因爲它可能是不安全的,因爲對其他不安全方法調用的某個其他函數的底層調用(如strerror(),(如果我的C內存很好))。

0

假設Object-C應用程序可以通過命令行運行,一個更簡單(也更少問題)的方法是使用java.lang.Runtime.exec(...)方法之一啓動它。

JNI充滿了複雜性和穩定性問題,如果可以的話,最好避免它。

編輯:OP已解釋說,這是一個「小部件」而不是命令行應用程序。這使得更難避免使用JNI。但我仍然認爲你應該嘗試。例如,您可以考慮將Objective-C小部件包裝到Objective-C應用程序中,該應用程序在新窗口中運行小部件。

+0

我不想運行一個object-c應用程序我試圖將這個小部件合併到我的應用程序中:http://developer.apple.com/Mac/library/documentation/GraphicsImaging/Reference/IKImagePicker_Class/IKImagePicker_Reference。html – Mike2012