缘起:最近做的RCP系统有如下需求,在系统中只允许一个应用程序窗口(实例)
使用过"金山词霸"或"有道词典"的朋友都知道。在"金山词霸"或"有道词典"已经运行了的情况下,两次点击上述程序的图标,那么它不会再运行另一个"金山词霸"或"有道词典",而是将已有的"金山词霸"或"有道词典"给激活,始终只能运行一个"金山词霸"或"有道词典"的实例。
在实际的程序开发中,通常会有这样的需求。
最近down了MyWork的源码下来研究,发现它是这么做的,主要是利用了ServerSocket的一个端口绑定的特性。代码很简单,如下:
//不允许启动第二个实例
final int port = ConfigHelper.getIntegerProperty("work.port");//①
if (port != 0) {
new Thread(){
public void run() {
try {
ServerSocket ss = new ServerSocket(port);
ss.accept();
}catch(BindException be){//②
//second instance not allowed
System.out.println("不允许启动第二个实例,请设置参数work.port");
System.exit(0);③
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
说明:
①这里为了不把port写死,通常会在配置文件中进行配置。硬编码灵活性也不好。因为很多时候这个端口可能被其他程序占用了。
②BindException试图将套接字绑定到本地地址和端口时发生错误的情况下,抛出此异常。这些错误通常发生在端口正在使用中或无法分配所请求的本地地址时。
③终止正在运行的Java虚拟机。参数用作状态码;根据惯例,非零的状态码表示异常终止。
④上面的代码在RCP中,当然是放在继承自AbstractUIPlugin类的插件类中的start方法当中了。记得要先调用
super.start(context);如下:
/**
* This method is called upon plug-in activation
*/
public void start(BundleContext context) throws Exception {
super.start(context);
//删除安装遗留文件Hidecmd.exe
if(new File("Hidecmd.exe").exists())
new File("Hidecmd.exe").delete();
//不允许启动第二个实例
final int port = ConfigHelper.getIntegerProperty("work.port");
if (port != 0) {
new Thread(){
public void run() {
try {
ServerSocket ss = new ServerSocket(port);
ss.accept();
}catch(BindException be){
//second instance not allowed
System.out.println("不允许启动第二个实例,请设置参数work.port");
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
⑤个人觉得这个方法不一定是最好的。但是也是一个思路。
⑥.既然无意中看到了这个问题,就在网上搜索了一番。参考博客如下:
如何做到像《金山词霸》一样只运行一个实例(转_孙鑫老师原创)
http://hi.baidu.com/piaoliugogo/blog/item/424f03003c32e008728da519.html
让程序只运行一个实例的4种方法
http://blog.csdn.net/magictong/archive/2008/12/25/3603015.aspx