JavaFX 单实例应用程序

标签 java javafx exe platform stage

尝试在用户“关闭”程序时单击所有退出按钮以使其不再有托盘图标。

我调用了 Platform.setImplicitExit(false);所以程序仍然在后台运行。

我正在尝试学习如何做到这一点,以便当用户重新单击运行 jar 的 .exe 文件时,它不会运行新程序,而是重新显示正在后台运行的程序。

 Platform.setImplicitExit(false);

最佳答案

这是基于博文中的解决方案:Java Single Instance Application .

解决方案使用“套接字技术”:

With this technique we start listening on a port, only one process can listen on a socket so after first instance of our application binds itself to the socket other instances will get BindException, which means we are already running.

Cons of this approach is that some virus scanners will give a warning when an application starts listening on a socket, depending on your user base this could be interpreted badly. You should pick a port number thats not commonly used and high or you won't even get a single instance of your application running.

在示例中,我们为应用程序实例创建了一个唯一的实例ID,并记录了一些选项。

unique

  • Minimize 会将窗口最小化。
  • Hide 将隐藏它(因此它不会显示为最小化,但该应用程序会继续运行)。
  • Exit 将结束申请流程。

窗口上的操作系统关闭按钮将关闭应用程序窗口,但应用程序进程将继续在后台运行(因此它与“隐藏”按钮的作用相同)。

当您启动一个应用程序实例时,它会打开一个套接字并监听它。

当您尝试启动另一个应用程序实例时,它将尝试绑定(bind)到监听套接字。如果它不能绑定(bind),那么它知道已经有一个应用程序实例在那个套接字上运行。如果检测到另一个实例,则会通过套接字向现有实例发送一条消息,导致现有实例取消隐藏或取消最小化自身,并尝试将其舞台置于最前面。

请不要滥用这个,有很多我不喜欢的隐藏在后台的程序。

import javafx.application.*;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;

public class SingleInstanceApp extends Application {

    private static final int SINGLE_INSTANCE_LISTENER_PORT = 9999;
    private static final String SINGLE_INSTANCE_FOCUS_MESSAGE = "focus";

    private static final String instanceId = UUID.randomUUID().toString();

    // We define a pause before focusing on an existing instance
    // because sometimes the command line or window launching the instance
    // might take focus back after the second instance execution complete
    // so we introduce a slight delay before focusing on the original window
    // so that the original window can retain focus.
    private static final int FOCUS_REQUEST_PAUSE_MILLIS = 500;

    private Stage stage;

    public void init() {
        CountDownLatch instanceCheckLatch = new CountDownLatch(1);

        Thread instanceListener = new Thread(() -> {
            try (ServerSocket serverSocket = new ServerSocket(SINGLE_INSTANCE_LISTENER_PORT, 10)) {
                instanceCheckLatch.countDown();

                while (true) {
                    try (
                            Socket clientSocket = serverSocket.accept();
                            BufferedReader in = new BufferedReader(
                                    new InputStreamReader(clientSocket.getInputStream()))
                    ) {
                        String input = in.readLine();
                        System.out.println("Received single instance listener message: " + input);
                        if (input.startsWith(SINGLE_INSTANCE_FOCUS_MESSAGE) && stage != null) {
                            Thread.sleep(FOCUS_REQUEST_PAUSE_MILLIS);
                            Platform.runLater(() -> {
                                System.out.println("To front " + instanceId);
                                stage.setIconified(false);
                                stage.show();
                                stage.toFront();
                            });
                        }
                    } catch (IOException e) {
                        System.out.println("Single instance listener unable to process focus message from client");
                        e.printStackTrace();
                    }
                }
            } catch(java.net.BindException b) {
                System.out.println("SingleInstanceApp already running");

                try (
                        Socket clientSocket = new Socket(InetAddress.getLocalHost(), SINGLE_INSTANCE_LISTENER_PORT);
                        PrintWriter out = new PrintWriter(new OutputStreamWriter(clientSocket.getOutputStream()))
                ) {
                    System.out.println("Requesting existing app to focus");
                    out.println(SINGLE_INSTANCE_FOCUS_MESSAGE + " requested by " + instanceId);
                } catch (IOException e) {
                    e.printStackTrace();
                }

                System.out.println("Aborting execution for instance " + instanceId);
                Platform.exit();
            } catch(Exception e) {
                System.out.println(e.toString());
            } finally {
                instanceCheckLatch.countDown();
            }
        }, "instance-listener");
        instanceListener.setDaemon(true);
        instanceListener.start();

        try {
            instanceCheckLatch.await();
        } catch (InterruptedException e) {
            Thread.interrupted();
        }
    }

    public void stop() {
        System.out.println("Exiting instance " + instanceId);
    }

    @Override
    public void start(Stage stage) throws Exception{
        this.stage = stage;

        System.out.println("Starting instance " + instanceId);

        Platform.setImplicitExit(false);

        Button minimize = new Button("Minimize");
        minimize.setOnAction(event -> stage.setIconified(true));

        Button hide = new Button("Hide");
        hide.setOnAction(event -> stage.hide());

        Button exit = new Button("Exit");
        exit.setOnAction(event -> Platform.exit());

        Label instance = new Label(instanceId);

        Pane layout = new VBox(10, instance, new HBox(10, minimize, hide, exit));
        layout.setPadding(new Insets(10));

        Scene scene = new Scene(layout);
        stage.setScene(scene);
        stage.show();
    }

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

相关问题

如果此答案中提供的解决方案的变体和可能的替代方法包含进一步讨论:

关于JavaFX 单实例应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41051127/

相关文章:

java - 用外部程序控制 Oracle Forms

java - 我可以将java返回的hashmap存储在机器人框架变量中吗?

java - 在独立的 JavaFX 应用程序中包含图标

.net - 如果文件用作引用程序集,是否有理由创建 .net DLL 而不是 EXE?

windows - 在命令提示符下运行 .exe 的 Bat 文件

java - 在 Java 中编写多个 if 语句的更好方法

java - 导入包会改变类的可见性吗?

android - Javafx和Android-Api

java - Jar 到 dmg 转换

vb6 - 从 Visual Basic 6 执行 .exe