JavaFX 单实例应用程序 [英] JavaFX Single Instance Application

查看:55
本文介绍了JavaFX 单实例应用程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

试图在用户关闭"程序时单击所有退出按钮,以便不再有托盘图标.

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

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

 Platform.setImplicitExit(false);

解决方案

这是基于博客文章中的解决方案:

  • Minimize 将最小化窗口.
  • Hide 将隐藏它(因此它不会显示为最小化,但应用程序仍在运行).
  • Exit 将结束申请过程.

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

当你启动一个应用实例时,它会打开一个套接字并监听它.

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

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

import javafx.application.*;导入 javafx.geometry.Insets;导入 javafx.scene.Scene;导入 javafx.scene.control.*;导入 javafx.scene.layout.*;导入 javafx.stage.Stage;导入 java.io.*;导入 java.net.InetAddress;导入 java.net.ServerSocket;导入 java.net.Socket;导入 java.util.UUID;导入 java.util.concurrent.CountDownLatch;公共类 SingleInstanceApp 扩展应用程序 {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();//我们在关注现有实例之前定义一个暂停//因为有时命令行或窗口启动实例//可能会在第二个实例执行完成后重新获得焦点//所以我们在关注原始窗口之前引入了一个轻微的延迟//使原来的窗口可以保持焦点.private static final int FOCUS_REQUEST_PAUSE_MILLIS = 500;私人舞台;公共无效初始化(){CountDownLatch instanceCheckLatch = new CountDownLatch(1);线程实例监听器 = 新线程(() -> {试试 (ServerSocket serverSocket = new ServerSocket(SINGLE_INSTANCE_LISTENER_PORT, 10)) {instanceCheckLatch.countDown();而(真){尝试 (Socket clientSocket = serverSocket.accept();BufferedReader in = new BufferedReader(新的 InputStreamReader(clientSocket.getInputStream()))){字符串输入 = in.readLine();System.out.println("收到单实例监听消息:" + input);if (input.startsWith(SINGLE_INSTANCE_FOCUS_MESSAGE) && stage != null) {Thread.sleep(FOCUS_REQUEST_PAUSE_MILLIS);Platform.runLater(() -> {System.out.println("到前面" + instanceId);stage.setIconified(false);舞台表演();舞台.toFront();});}} catch (IOException e) {System.out.println("单实例监听器无法处理来自客户端的焦点消息");e.printStackTrace();}}} catch(java.net.BindException b) {System.out.println("SingleInstanceApp 已经在运行");尝试 (Socket clientSocket = new Socket(InetAddress.getLocalHost(), SINGLE_INSTANCE_LISTENER_PORT);PrintWriter out = new PrintWriter(new OutputStreamWriter(clientSocket.getOutputStream()))){System.out.println("请求现有应用程序聚焦");out.println(SINGLE_INSTANCE_FOCUS_MESSAGE + " 请求 " + instanceId);} catch (IOException e) {e.printStackTrace();}System.out.println("正在中止执行实例" + instanceId);Platform.exit();} 捕获(异常 e){System.out.println(e.toString());} 最后 {instanceCheckLatch.countDown();}}, "实例监听器");instanceListener.setDaemon(true);instanceListener.start();尝试 {instanceCheckLatch.await();} catch (InterruptedException e) {Thread.interrupted();}}公共无效停止(){System.out.println("退出实例" + instanceId);}@覆盖公共无效开始(阶段阶段)抛出异常{this.stage = 阶段;System.out.println("起始实例" + instanceId);Platform.setImplicitExit(false);按钮最小化 = new Button("最小化");minimum.setOnAction(event -> stage.setIconified(true));按钮隐藏 = new Button("隐藏");hide.setOnAction(event -> stage.hide());按钮退出 = new Button("退出");exit.setOnAction(event -> Platform.exit());标签实例 = 新标签(instanceId);窗格布局 = new VBox(10, instance, new HBox(10, 最小化, 隐藏, 退出));layout.setPadding(new Insets(10));场景场景 = 新场景(布局);stage.setScene(场景);舞台表演();}公共静态无效主(字符串 [] args){发射(参数);}}

Trying to make it so when the user "closes" the program clicking all the exit buttons so there is no more tray icon.

I called Platform.setImplicitExit(false); so the program still runs in backround.

I am trying to learn how to make it so when the user re-clicks the .exe files which runs the jar,instead of running a new program it re-shows that one that is running in background.

 Platform.setImplicitExit(false);

解决方案

This is based upon the solution in the blog post: Java Single Instance Application.

The solution uses the "Socket Technique":

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.

In the sample, we have created a unique instance id for the application instance and recorded some options.

  • Minimize will minimize the window.
  • Hide will hide it (so it doesn't show as minimized, but the app remains running).
  • Exit will end the application process.

The OS close button on the window will close the application window, but the application process will continue running in the background (so it acts the same as the "Hide" button).

When you start an application instance, it will open a socket and listen on it.

When you try to start another application instance it will try to bind to the listening socket. If it cannot bind, then it knows there is already an application instance running on that socket. If another instance is detected, a message is sent across the socket to the existing instance causing the existing instance to unhide or un-minimize itself and try to bring its stage to the front.

Please don't abuse this, there are many programs that hide in the background that I don't like.

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 单实例应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆