详细介绍如何开始使用JNI的大多数文档描述了如何使用X-Code构建新的JNI应用程序。任何人都可以链接到我在如何使用JNI与现有应用程序中的Objective-C接口的描述。
答案 0 :(得分: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); }
}
}
}
请注意由IBM提供的底层现有API QWCRDTAA的单行调用;其余的是以Java为中心的包装,这是进行调用和处理结果所必需的。
另外,要非常小心,您调用的是线程安全的,或者保护代码不受Java层中全局的并发调用的影响,或者使用O / S层中的互斥锁保护代码。
PS:请注意,非线程安全的本机代码全局非线程安全;您必须阻止与所有其他非线程安全的本机代码并发调用,而不仅仅是您正在调用的一个方法。这是因为它可能是不安全的,因为对其他不安全方法调用的其他函数的底层调用(如strerror(),(如果我的C内存很好))。
答案 1 :(得分: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。开发人员非常乐于助人。
答案 2 :(得分:0)
假设可以通过命令行运行Object-C应用程序,那么使用java.lang.Runtime.exec(...)
方法之一启动它就会更简单(也更少问题)。
编辑:OP已经解释说这是一个“小部件”而不是命令行应用程序。这使得避免使用JNI变得更加困难。但我仍然认为你应该尝试。例如,您可以考虑将Objective-C小部件包装在Objective-C应用程序中,该应用程序在新窗口中运行小部件。