这是基于博客文章中的解决方案:Java单实例应用程序 https://nakkaya.com/2009/04/12/java-single-instance-application/.
该解决方案使用“套接字技术”:
通过这种技术,我们开始侦听端口,只有一个进程可以侦听套接字,因此在应用程序的第一个实例将自身绑定到套接字后,其他实例将得到 BindException,这意味着我们已经在运行。
这种方法的缺点是,当应用程序开始侦听套接字时,某些病毒扫描程序会发出警告,这可能会被错误地解释,具体取决于您的用户群。您应该选择一个不常用且较高的端口号,否则您甚至无法运行应用程序的单个实例。
在示例中,我们为应用程序实例创建了一个唯一的实例 ID 并记录了一些选项。
-
Minimize将最小化窗口。
-
Hide将隐藏它(因此它不会显示为最小化,但应用程序仍保持运行)。
-
Exit将结束申请流程。
窗口上的操作系统关闭按钮将关闭应用程序窗口,但应用程序进程将继续在后台运行(因此其作用与“隐藏”按钮相同)。
当您启动应用程序实例时,它将打开一个套接字并侦听它。
当您尝试启动另一个应用程序实例时,它将尝试绑定到侦听套接字。如果它无法绑定,那么它就知道该套接字上已经有一个应用程序实例正在运行。如果检测到另一个实例,则会通过套接字向现有实例发送一条消息,导致现有实例取消隐藏或取消最小化自身,并尝试将其阶段置于前面。
请不要滥用这个,有很多我不喜欢的程序隐藏在后台。
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);
}
}
相关问题
包含进一步的讨论,如果此答案中提供的解决方案的变体以及可能的替代方法:
- 处理多个应用程序实例 https://stackoverflow.com/questions/71429246/dealing-with-multiple-application-instances/71431379#71431379