如何从 JNI 方法正确调用 NSOpenPanel 以在正确的线程上运行?

问题描述 投票:0回答:1

由于我不熟悉 Objective-C 和 JNF,我只能让 JNI 类部分执行。到目前为止我尝试过的方法如下:

这里是 Open.java,它是一个更重要的 Java Swing 应用程序的 MCV,它应该有一个更好看、功能更好的打开文件对话框,而不是不合理的

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

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

#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!

这是可以理解的,因为面板应该在 UI 线程上打开。我相信我所要做的就是确保 NSOpenPanel 创建的窗口是 JFrame 的子窗口并在 UI 线程上打开。我相信这至少意味着我必须执行一个 Java 函数才能获取 JFrame 的 NSWindow。

java macos cocoa java-native-interface
1个回答
0
投票

您可以使用dispatch_async() 和dispatch_sync() 在UI 线程上执行操作。 NSObject 还包含一个方法

performSelectorOnMainThread:withObject:waitUntilDone:
,它将在主线程上执行选择器。

这是使用 Java Objective-C 桥的要点,它可以执行与此类似的操作:

https://gist.github.com/anonymous/3966989

对于您的具体问题,您可以将方法主体包装在dispatch_async中

JNIEXPORT void JNICALL Java_NativeOpenFileDialog_run (JNIEnv *thisEnv, jclass jcls) {
dispatch_async(dispatch_get_main_queue(), ^{
  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];
});

}

当然,这只是打开对话框,不会对用户选择的文件执行任何操作,因此您需要在此基础上进行构建。

© www.soinside.com 2019 - 2024. All rights reserved.