大多數詳細介紹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. 如何用PhoneGap包裝現有的iOS應用程序
- 2. 包裝AngularJs應用程序
- 3. 包裝應用程序
- 4. 應用程序包裝
- 5. Android應用程序包裝
- 6. 包裝java應用程序
- 7. JNI的Java應用程序
- 8. JNI - 應用程序崩潰
- 9. JNI應用程序狀態
- 10. 分析JNI應用程序
- 11. 包裝電子在現有的應用程序
- 12. Android應用程序未安裝。現有的同名軟件包
- 13. 如何將現有的java應用程序包裝到servelet中?
- 14. 使用Jni包裝C++庫
- 15. Wrapp現有庫(.so)jni的Android應用程序
- 16. 用Ionic包裝部署應用程序
- 17. 將應用程序包裝在另一個應用程序中
- 18. 其他應用程序中的包裝Rails應用程序
- 19. C#在另一個應用程序中包裝應用程序
- 20. 如何在android ndk中編譯dsplink應用程序以開發jni包裝器
- 21. 無法在獨立Java應用程序上運行PJSIP-JNI包裝器
- 22. 安裝程序不會覆蓋現有的應用程序
- 23. Xamarin如何實現到應用程序igdb API包裝安裝包
- 24. Android JNI APK包裝
- 25. 包安裝程序崩潰安裝我的應用程序
- 26. OSX - 編譯Java JNI應用程序以僅安裝JRE運行
- 27. 包括現有的應用程序如在自己的應用程序片段
- 28. 包裝Java Web應用程序的EXE
- 29. Android - 包裝2應用程序一起
- 30. Maven包裝web應用程序
非常感謝您的建議,我實際上一直在與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