Java保證只有一個實例運行,再次啓動將激活前一個實例

最近工作中碰到這個問題,用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.     } 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章