2013-02-02 38 views
10

Tôi đang sử dụng JNI để gọi một phương thức java tĩnh để tạo ra một Swing JFrame và hiển thị nó. Mã này khá đơn giản và mã Java hoạt động độc lập (tức là java StartAWT thực hiện những gì cần) trong khi được gọi từ C bằng cách sử dụng JNI, quy trình bị treo.Java JNI: Tạo cửa sổ xoay bằng JNI từ C

Tôi đang sử dụng JDK 1.7.0_09 trên Mac OS X 10.8 Mountain Lion.

Đây là mã C Tôi đang sử dụng để gọi phương thức tĩnh:

JavaVM* jvm; 
JNIEnv* env = create_vm(&jvm); 

jclass class = (*env)->FindClass(env, "StartAWT"); 
jmethodID method = (*env)->GetStaticMethodID(env, class, "run", "()V"); 

(*env)->CallStaticVoidMethod(env, class, method); 

(*jvm)->DestroyJavaVM(jvm); 

Lớp StartAWT trông như thế này:

public class StartAWT { 

    public static class Starter implements Runnable { 
     public void run() { 
      System.out.println("Runnning on AWT Queue."); 

      JFrame.setDefaultLookAndFeelDecorated(true); 
      JFrame frame = new JFrame("That's a frame!"); 
      JLabel label = new JLabel("A Label"); 
      frame.getContentPane().add(label); 

      frame.pack(); 
      frame.setVisible(true); 
     } 
    } 

    public static class GUI implements Runnable { 
     public void run() { 
      try { 
       System.out.println("Going to put something on the AWT queue."); 
       SwingUtilities.invokeAndWait(new Starter()); 
      } catch (Exception exc) { 
       throw new RuntimeException(exc); 
      } 
     } 
    } 

    public static void run() { 
     Thread gui = new Thread(new GUI()); 
     gui.start(); 
    } 
} 

Khi tôi khởi động ứng dụng, tôi thấy Going to put something on the AWT queue nhưng không phải là Running on AWT Queue.

Tôi tin rằng Máy ảo trong quy trình C của tôi không có hàng đợi sự kiện AWT nhưng tôi không biết cách thiết lập để có một (hoặc tôi chắc chắn rằng đây là lý do).

Điều gì sẽ được thực hiện để hiển thị GUI dựa trên AWT bằng JNI?

-

EDIT: Tôi đã chèn vòng để xem những chủ đề còn sống và không được (có thể được nhìn thấy trong this gist). Trong phiên bản này tôi thực hiện lời gọi SwingUtilities.invokeAndWait trong một chủ đề khác. Kết quả: Chủ đề chính còn sống (C). Luồng đầu tiên được gửi đi bởi Java (không phải luồng chính) là còn sống; các chủ đề làm các cuộc gọi invokeAndWait bị chặn (Tôi không nghĩ rằng invokeAndWait thậm chí đã trở lại), chức năng cần được chạy trên EventQueue thậm chí không được nhập.

Tôi cũng đã cố gắng gọi SwingUtilities.invokeAndWait trực tiếp, mà sẽ cung cấp được thông báo sau:

2013-02-02 13:50:23.629 swing[1883:707] Cocoa AWT: Apple AWT Java VM was loaded on first thread -- can't start AWT. (
    0 liblwawt.dylib      0x0000000117e87ad0 JNI_OnLoad + 468 
    1 libjava.dylib      0x00000001026076f1  Java_java_lang_ClassLoader_00024NativeLibrary_load + 207 
    2 ???         0x000000010265af90 0x0 + 4335185808 
) 

Đây cũng là những gì tôi đã đọc trong các câu hỏi khác ở đây trên StackOverflow, chẳng hạn như một trong những gợi ý trong các ý kiến phía dưới. Tuy nhiên, tôi không thể tìm thấy một giải pháp cho vấn đề ban đầu. Có thể đáng chú ý là sau khi thông báo trên xuất hiện, luồng chính vẫn còn sống, tức là không có quá trình bế tắc cũng như sự cố.

-

EDIT: Tôi đã thử nghiệm mã trên Linux, nơi nó được làm việc như mong đợi. Vì vậy, tôi tin rằng đây là một vấn đề với Mac OS X với Cocoa AWT, nhưng tôi không biết làm thế nào để phá vỡ nó.

-

EDIT: Tôi cũng đã cố gắng di chuyển toàn bộ gọi của JVM lên một chủ đề quê hương mới. Điều này làm việc trên Mac OS X 10.6 với Apple 32 bit (1.6.0_37), nhưng kết quả trong cùng một bế tắc như được mô tả ở trên. Trên Mac OS X 10.8, điều này tệ hơn, ứng dụng sẽ đổ xuống với thông điệp duy nhất "Trace/BPT trap: 5" (trong đó seems to be related to loading dynamic libraries).

Tôi cũng đã cố gắng bundling nhị phân như mô tả in this Q&A, nhưng sự ra mắt không thành công với thông điệp lsopenurlswithrole() failed with the message -10810, đó là một lỗi không rõ, theo Táo Launch Services Reference. Cái sau cũng xảy ra mà không cố gắng sử dụng AWT (lời gọi JVM chỉ thất bại).

+0

Xem thêm [Hỏi đáp] này (http://stackoverflow.com/q/8750690/230513). – trashgod

+0

Cảm ơn, tôi đã kiểm tra phần Hỏi & Đáp và chơi với ứng dụng của tôi; nhưng nó không hoạt động sau khi tất cả (như trong câu hỏi khác, không có giải pháp được đưa ra có quá). – scravy

+0

Tôi nhận được kết quả tương tự; Tôi chỉ sử dụng 'JavaApplicationStub', nhưng tôi không biết nó hoạt động như thế nào. Tôi tự hỏi nếu 'JVM TI', trích dẫn [ở đây] (http://stackoverflow.com/a/14492574/230513), có bất cứ điều gì có liên quan. – trashgod

Trả lời

7

Cuối cùng tôi đã tìm được giải pháp.

Vấn đề không phải là trên luồng mà Máy ảo đang được tạo, vấn đề là trên luồng nào mà Hàng đợi sự kiện AWT đang được khởi tạo. Nói cách khác: Lần đầu tiên một lớp AWT được tải, nó có thể không được tải trên luồng chính. Do đó bước 1: Tải (ví dụ) java.awt.Component trên một chủ đề khác.

Nhưng bây giờ EventQueue sẽ chặn, vì nó đại biểu làm việc cho hàng đợi sự kiện chính Cocoa không chạy - chắc chắn đủ, vì nó sẽ chỉ chạy trên chủ đề chính và chuỗi chính là ứng dụng của tôi. Do đó, chạy vòng lặp chính cần phải được bắt đầu vào chủ đề chính:

void 
runCocoaMain() 
{ 
    void* clazz = objc_getClass("NSApplication"); 
    void* app = objc_msgSend(clazz, sel_registerName("sharedApplication")); 

    objc_msgSend(app, sel_registerName("run")); 
} 

tôi phải liên kết ứng dụng của tôi với khuôn khổ Cocoa và bao gồm <objc/objc-runtime.h>. Các chủ đề chính bị chặn sau khi cuộc gọi đến runCocoaMain (kể từ khi vòng lặp sự kiện đang chạy ở đó), do đó, một trong những nhu cầu để nghỉ mát đến một thread cho các ứng dụng riêng của mình.

Sau khi chạy EventQueue bằng đoạn mã trên, việc tải lớp AWT trên chuỗi khác sẽ thành công và bạn có thể tiếp tục ở đó.

+0

+1 cho sự kiên trì. – trashgod

+0

Cảm ơn bạn! Nó hoạt động như sự quyến rũ. :) –

+0

Không hoàn toàn hiểu. Bạn có thể vui lòng cho tôi biết cách "tải lớp AWT trên chủ đề khác" không? –

Các vấn đề liên quan