【问题标题】:Javafx deadlock when using setVIsible method with DragEvent使用带有 DragEvent 的 setVIsible 方法时的 Javafx 死锁
【发布时间】:2016-05-22 06:15:00
【问题描述】:

我有一个非常简单的拖放实现,它获取文件位置,然后在舞台上隐藏一些元素以显示有关文件的信息。但是,每当我尝试使用 setVisible() 或与舞台交互的任何其他命令时,我的程序就会完全陷入僵局。如果我在拖动文件后只有一个 println,它可以正常工作,但是当我有任何操作 UI 的逻辑时,它就会停止。

这是拖入文件后死锁的样子

"main@1" prio=5 tid=0x1 nid=NA waiting
  java.lang.Thread.State: WAITING
      at sun.misc.Unsafe.park(Unsafe.java:-1)
      at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
      at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
      at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:997)
      at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1304)
      at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:231)

这是获取拖动事件的最小化 Main 方法。

package sample;

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.stage.Stage;

import java.io.File;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        Scene scene = new Scene(root, 300, 275);
        scene.setOnDragOver(new EventHandler<DragEvent>() {
            @Override
            public void handle(DragEvent event) {
                Dragboard db = event.getDragboard();
                if (db.hasFiles()) {
                    event.acceptTransferModes(TransferMode.COPY);
                } else {
                    event.consume();
                }
            }
        });
        scene.setOnDragDropped(new EventHandler<DragEvent>() {
            @Override
            public void handle(DragEvent event) {

                Dragboard db = event.getDragboard();
                boolean success = false;
                if (db.hasFiles()) {
                    success = true;
                    String filePath = null;
                    for (File file:db.getFiles()) {
                        filePath = file.getAbsolutePath();
                        System.out.println(filePath);
                        Controller controller = new Controller();
                        //Part where it crashes,
                        controller.buttonTest.setVisible(false);
                    }
                }
                event.setDropCompleted(success);
                event.consume();
            }
        });
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(scene);
        primaryStage.show();
    }


    public static void main(String[] args) {
        launch(args);
    }
}

最小化控制器

package sample;
import javafx.scene.control.Button;

public class Controller {
    public Button buttonTest;
}

最小化的 fxml

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<GridPane alignment="center" hgap="10" vgap="10" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="sample.Controller">
   <columnConstraints>
      <ColumnConstraints />
   </columnConstraints>
   <rowConstraints>
      <RowConstraints />
   </rowConstraints>
   <children>
      <Button fx:id="buttonTest" mnemonicParsing="false" text="Test" />
   </children>
</GridPane>

我认为我可以通过将 dragEvent 拆分到它自己的线程中来处理这个问题,但是根据 javafx 中的并发规则,它无论如何都无法操纵舞台,因为所有图形都必须在主 javafxGUI 线程上。

希望有人能指出我正确的方向,谢谢!

【问题讨论】:

  • 什么是controller,你在哪里初始化它?
  • @James_D 抱歉忘记包含了,我在start方法之前初始化了,它是fxml文件的控制器。
  • 如果在start方法之前初始化fxml文件,它不可能是控制器。 fxml 文件的控制器是由 FXMLLoader 在您调用 load 时创建的,因此在您的 start 方法的第一行完成之前它甚至还没有被实例化。
  • @James_D Welp,我之前有 Controller controller = new Controller(); 行,它似乎正在工作。
  • edit 您的问题包含minimal reproducible example,而不是发布链接。至于访问控制器class,显然你可以这样做,但它没有帮助。您需要访问实际的控制器实例,而不是碰巧是同一类的其他任意对象。

标签: java multithreading javafx


【解决方案1】:

当您将某些内容拖放到窗口上时,您的代码会生成空指针异常,因为在您使用 Controller controller = new Controller(); 手动创建的 Controller 实例中从未初始化 button

当您调用load() 时,fxml 文件中定义的元素在与FXMLLoader 关联的控制器实例中初始化。所以我认为您在这里尝试做的是访问该控制器对象。 (我不知道你为什么要创建一个新的。)你可以通过创建一个FXMLLoader 实例并在其上调用getController() 来做到这一点,而不是使用静态加载方法:

@Override
public void start(Stage primaryStage) throws Exception{
    FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
    Parent root = loader.load();
    Controller controller = loader.getController();

    // ...

    scene.setOnDragDropped(new EventHandler<DragEvent>() {
        @Override
        public void handle(DragEvent event) {

            Dragboard db = event.getDragboard();
            boolean success = false;
            if (db.hasFiles()) {
                success = true;
                String filePath = null;
                for (File file:db.getFiles()) {
                    filePath = file.getAbsolutePath();
                    System.out.println(filePath);
                    // Controller controller = new Controller();
                    //Part where it crashes,
                    controller.buttonTest.setVisible(false);
                }
            }
            event.setDropCompleted(success);
            event.consume();
        }
    });

    // ...

}

【讨论】:

  • 谢谢你,使用控制器的 fxml 加载器实例修复了它,我对 Javafx 很陌生,所以我只是假设一个静态对象可以解决问题。我也很好奇是什么告诉你它是一个空指针。我是否在调试输出中查看了错误的线程。再次感谢您的帮助!
  • 不知道你在看什么:我只是运行它(没有调试器)并且控制台中有一个空指针异常(如预期的那样)。不知道您所说的“我只是假设一个静态对象可以解决问题”是什么意思:您没有任何静态变量(它wouldn't help if you did)。
猜你喜欢
  • 2019-11-22
  • 2019-05-04
  • 1970-01-01
  • 1970-01-01
  • 2018-12-23
  • 2014-05-24
  • 2021-02-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多