2009-11-03 48 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