大多数详细介绍JNI入门的文档都介绍了如何使用X-Code构建新的JNI应用程序。任何人都可以链接到我介绍如何使用JNI与Objective-C在现有应用程序中进行交互。用JNI包装现有应用程序
回答
注意:我已经完全重新从头开始写这个答案,现在我知道它确实有效;-)。使用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。开发人员非常有帮助。
您仍然需要编写某种类型的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内存很好))。
假设Object-C应用程序可以通过命令行运行,一个更简单(也更少问题)的方法是使用java.lang.Runtime.exec(...)
方法之一启动它。
JNI充满了复杂性和稳定性问题,如果可以的话,最好避免它。
编辑:OP已解释说,这是一个“小部件”而不是命令行应用程序。这使得更难避免使用JNI。但我仍然认为你应该尝试。例如,您可以考虑将Objective-C小部件包装到Objective-C应用程序中,该应用程序在新窗口中运行小部件。
我不想运行一个object-c应用程序我试图将这个小部件合并到我的应用程序中:http://developer.apple.com/Mac/library/documentation/GraphicsImaging/Reference/IKImagePicker_Class/IKImagePicker_Reference。html – Mike2012
- 1. 应用程序包装
- 2. 包装AngularJs应用程序
- 3. 包装应用程序
- 4. Android应用程序包装
- 5. 包装java应用程序
- 6. JNI的Java应用程序
- 7. 分析JNI应用程序
- 8. JNI应用程序状态
- 9. JNI - 应用程序崩溃
- 10. Android应用程序未安装。现有的同名软件包
- 11. 包装电子在现有的应用程序
- 12. 如何将现有的java应用程序包装到servelet中?
- 13. 如何在android ndk中编译dsplink应用程序以开发jni包装器
- 14. C#在另一个应用程序中包装应用程序
- 15. 其他应用程序中的包装Rails应用程序
- 16. 安装程序不会覆盖现有的应用程序
- 17. Xamarin如何实现到应用程序igdb API包装安装包
- 18. Android JNI APK包装
- 19. OSX - 编译Java JNI应用程序以仅安装JRE运行
- 20. 包安装程序崩溃安装我的应用程序
- 21. 包括现有的应用程序如在自己的应用程序片段
- 22. C#包装和web应用程序
- 23. 应用程序包安装com.apple.installer.pagecontroller erreur -1
- 24. 包装可穿戴应用程序
- 25. 围绕应用程序包装Applet
- 26. 在Android应用程序中包装HTML
- 27. 预包装Rails应用程序
- 28. Android - 包装2应用程序一起
- 29. 目标vuejs应用程序外包装
- 30. 包装Java Web应用程序的EXE
非常感谢您的建议,我实际上一直在与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
正确,你需要使用'-Djna.library.path = <目录,其中包含librococoa.dylib>' –
哈哈我被推迟了。谢谢。 – Mike2012