You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在JNI方法中正确调用NSOpenPanel并在主线程运行?

问题:Java Swing应用通过JNI调用Cocoa文件对话框时的主线程异常

我对Objective-C和JNI不熟悉,JNI类仅能部分执行,以下是我的尝试:

代码文件

Open.java(最小复现代码)

这是大型Java Swing应用的最小复现代码,目的是替代难用的java.awt.FileDialog或功能有限的javax.swing.JFileChooser,实现更美观完善的打开文件对话框:

import java.awt.event.*;
import javax.swing.*;
public class Open extends JFrame {
   public Open () {
      getContentPane().add(new JButton(new AbstractAction() {
         public void actionPerformed (ActionEvent e) {
            try {
               NativeOpenFileDialog.run();
            } catch (Throwable ex) {
               System.err.println(ex.getMessage());
            }
         }
      }));
      pack();
      setVisible(true);
   }
   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() { new Open(); }
      });
   }
}

NativeOpenFileDialog.java(JNI桥接类)

import javax.swing.*;

public class NativeOpenFileDialog {
   static {
       System.loadLibrary("natopndlg");
   };
   public static native void run ();
   public static void main (String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() { NativeOpenFileDialog.run(); }
      });

   }
}

NativeOpenFileDialog.m(Objective-C实现)

#import <Cocoa/Cocoa.h>

#include <jni.h>
#include "NativeOpenFileDialog.h"

JNIEXPORT void JNICALL Java_NativeOpenFileDialog_run (JNIEnv *thisEnv, jclass jcls) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSOpenPanel *panel = [NSOpenPanel openPanel];
    [panel setCanChooseFiles:YES];
    [panel setCanChooseDirectories:YES];
    [panel setAllowsMultipleSelection:YES];
    if ([panel runModal] != NSModalResponseOK)
        NSLog(@"User did not press OK");
    [pool release];
}

Makefile(Xcode 15.2环境)

SDK = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk

Open.class: Open.java NativeOpenFileDialog.java NativeOpenFileDialog.m
    javac -h . NativeOpenFileDialog.java
    javac Open.java
    gcc -dynamiclib -o libnatopndlg.dylib -framework Cocoa -isysroot $(SDK) -I $(JAVA_HOME)/include -I $(JAVA_HOME)/include/darwin -fobjc-exceptions -std=c99 NativeOpenFileDialog.m

执行步骤与异常

执行make编译后,运行命令:

java Open

点击按钮时抛出异常:

NSInternalInconsistencyException', reason: 'NSWindow should only be instantiated on the main thread!

问题分析与思路

这个异常是合理的,因为Cocoa的UI组件必须在主线程(UI线程)上创建。我认为需要确保NSOpenPanel在主线程打开,并且作为JFrame的子窗口,这至少需要实现一个Java函数来获取JFrame对应的NSWindow实例。


内容的提问来源于stack exchange,提问作者Jeff Holt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 03:12:27