最近工作中碰到这个问题,用Java写的一个系统托盘,要求只能有一个实例,当再次启动程序的时候,需要激活并显示已经启动的实例。

网上搜了一下,一个实例的问题网上已经有解决办法(使用文件锁),激活前一个实例的问题却没有头绪,有人说用Win32 API,但搜了好多,搜到的都是VB,VC++等来实现的,没有用Java的;还有一个方法是使用Socket,这个尝试了下,确实可行,但缺点是需要占用一个端口。

这里我把两个问题写到一个例子里,供有需要的朋友参考:

其中:有一个系统托盘实例:SystemTray tray;一个窗体对象:JFrame frame;

在启动系统托盘的main()方法里,代码如下:

 

 
  
  1. public class AppTest { 
  2.     private JFrame frame;               //窗体 
  3.     private SystemTray tray;            //系统托盘 
  4.     private MyServer server;    //服务端socket 
  5.     public AppTest() { 
  6.         init(); 
  7.     } 
  8.     private void init(){ 
  9.         //如果当前操作系统不支持系统托盘,则退出 
  10.         if(!SystemTray.isSupported()){ 
  11.             JOptionPane.showMessageDialog(null"对不起,当前操作系统不支持系统托盘!"); 
  12.             System.exit(0); 
  13.         } 
  14.         tray = SystemTray.getSystemTray(); 
  15.         Image p_w_picpath = null
  16.         try { 
  17.             p_w_picpath = ImageIO.read(this.getClass().getClassLoader().getResourceAsStream("com/blog/icons.png")); 
  18.         } catch (IOException e) { 
  19.             JOptionPane.showMessageDialog(null, e.getMessage()); 
  20.             System.exit(0); 
  21.         } 
  22.         final TrayIcon trayIcon = new TrayIcon(p_w_picpath); 
  23.         try { 
  24.             tray.add(trayIcon); 
  25.         } catch (AWTException e) { 
  26.             JOptionPane.showMessageDialog(null, e.getMessage()); 
  27.             System.exit(0); 
  28.         } 
  29.         PopupMenu menu = new PopupMenu(); 
  30.         MenuItem mi = new MenuItem("Exit"); 
  31.         mi.addActionListener(new ActionListener() { 
  32.             @Override 
  33.             public void actionPerformed(ActionEvent e) { 
  34.                 frame.setVisible(false); 
  35.                 frame.dispose(); 
  36.                 frame = null
  37.                 tray.remove(trayIcon); 
  38.                 tray = null
  39.                 server.closeServer(); 
  40.                 System.exit(0); 
  41.             } 
  42.         }); 
  43.         menu.add(mi); 
  44.         trayIcon.setPopupMenu(menu); 
  45.          
  46.         frame = new JFrame("AppTest"); 
  47.         frame.setSize(new Dimension(200200)); 
  48.         frame.setVisible(true); 
  49.     } 
  50.     /** 
  51.      * 显示/隐藏 主窗体 
  52.      * */ 
  53.     public void showHideFrame(boolean isVisible){ 
  54.         frame.setVisible(isVisible); 
  55.         frame.setExtendedState(JFrame.NORMAL); 
  56.     } 
  57.      
  58.     /** 
  59.      * 注入server对象,关闭server时使用 
  60.      * @param server 
  61.      */ 
  62.     public void setServerSocket(MyServer server){ 
  63.         this.server = server; 
  64.     } 
  65.     //检查是否获得锁,true:获得锁,说明是第一次执行;false:没有取得锁,说明已经有一个程序在执行 
  66.     public static boolean checkLock() { 
  67.         FileLock lock = null
  68.         RandomAccessFile r = null
  69.         FileChannel fc = null
  70.         try { 
  71.             // 在临时文件夹创建一个临时文件,锁住这个文件用来保证应用程序只有一个实例被创建. 
  72.             File sf = new File(System.getProperty("java.io.tmpdir") + "lock.single"); 
  73.             sf.deleteOnExit(); 
  74.             sf.createNewFile(); 
  75.             r = new RandomAccessFile(sf, "rw"); 
  76.             fc = r.getChannel(); 
  77.             lock = fc.tryLock(); 
  78.             if (lock == null||!lock.isValid()) { 
  79.                 // 如果没有得到锁,则程序退出. 
  80.                 // 没有必要手动释放锁和关闭流,当程序退出时,他们会被关闭的. 
  81.                 return false
  82.             } 
  83.         } catch (Exception e) { 
  84.             e.printStackTrace(); 
  85.         } 
  86.         return true
  87.     } 
  88.     public static void main(String[] args) { 
  89.         //检查文件锁,确保只有一个实例运行 
  90.         if(!checkLock()){ 
  91.             //告知上一个程序激活主窗口 
  92.             try { 
  93.                 new Socket(InetAddress.getLocalHost(),60098); 
  94.             } catch (Exception e) { 
  95.                 e.printStackTrace(); 
  96.             } 
  97.             //退出当前程序 
  98.             System.exit(0); 
  99.         } 
  100.          
  101.         AppTest app = new AppTest(); 
  102.         new MyServer(app); 
  103.     } 
  104.      
  105.     static class MyServer{ 
  106.         private ServerSocket server;//当前的socket 
  107.         private AppTest tray;       //保存的前一个托盘实例 
  108.         public MyServer(AppTest tray) { 
  109.             this.tray = tray; 
  110.             tray.setServerSocket(this); 
  111.             initServerSocket(); 
  112.         } 
  113.         private void initServerSocket(){ 
  114.             try { 
  115.                 server = new ServerSocket(60098); 
  116.                 while(true){ 
  117.                     if(server.isClosed()){ 
  118.                         break
  119.                     } 
  120.                     //如果监听到一个socket连接,说明程序启图再次打开一个实例,此时显示前一个窗体 
  121.                     Socket socket = server.accept(); 
  122.                     tray.showHideFrame(true); 
  123.                     socket.close(); 
  124.                 } 
  125.             } catch (IOException e) { 
  126.                 e.printStackTrace(); 
  127.             } 
  128.         } 
  129.         public void closeServer(){ 
  130.             try { 
  131.                 server.close(); 
  132.             } catch (IOException e) { 
  133.                 e.printStackTrace(); 
  134.             } 
  135.         } 
  136.     }