当前位置: 首页 > 工具软件 > RSSOwl > 使用案例 >

分析RSSOwl如何做到只运行一个实例,并且可以激活

祁俊喆
2023-12-01

前几天自己发表的只运行一个实例的文章,感谢Pande的留意,并提出宝贵意见,他推荐的正好是我想找的RSSOwl源码,以下是我分析RSSOwl如何做到只运行一个实例并且激活的,如有错误,请拍砖。

http://sourceforge.krugle.com/kse/files/cvs/cvs.sourceforge.net/rssowl/RSSOwl/src/java/net/sourceforge/rssowl/controller/RSSOwlLoader.java

根据 RSSOwlLoader.java 中的

[code]
private static void startupProcess( String[] args )
{
  ...

  if( !System.getProperties().containsKey( PROPERTY_ALLOW_MULTI_INSTANCES )&&StartupManager.isRSSOwlRunning( feedArgument ) )

  ...

}
[/code]

追踪到 StartupManager.java 见下:

http://sourceforge.krugle.com/kse/files/cvs/cvs.sourceforge.net/rssowl/RSSOwl/src/java/net/sourceforge/rssowl/controller/thread/StartupManager.java

追踪到 StartupManager.isRSSOwlRunning( String message ) 用途为建立ServerSocket,监听本地8794端口

[code]
static ServerSocket applLockSocket;

public static boolean isRSSOwlRunning( String message )
{

  try{ applLockSocket=new ServerSocket( 8794, 50, InetAddress.getByName( "127.0.0.1" ) );  //1987年9月4日出生?

       handleSocketUnBound();  //

       return false;
     }
  catch( java.net.BindException e )    //Another instance already running
       {
         ...

         handleSocketBound( message );  //

         return true;
       } 
  catch( IOException e )  //Other Error
       {
         ...

         return false;
       }
}
[/code]

追踪到 StartupManager.handleSocketUnBound() 和 StartupManager.handleSocketBound( String message )

[code]
private static void handleSocketBound( String message )
{
  Socket socket;

  try{ socket=new Socket( InetAddress.getByName( "127.0.0.1" ), 8794 );

       PrintWriter writer=new PrintWriter( new OutputStreamWriter( socket.getOutputStream() ) );

       writer.println( ( message!=null&&message.length()>0 ) ? message : DEFAULT_MESSAGE );

       writer.flush();
     }
  catch( UnknownHostException e )
       {
         ...
       }
  catch( IOException e )
       {
         ...
       }
}
[/code]

[code]
/**
 * Server not yet running. Start it and listen for incoming messages.
 */
private static void handleSocketUnBound()
{
  listen();  //转向,为了对称好看?还是为了兼容?
}

/**
 * Listen for incoming messages.看看接受的连接发送了什么内容
 */
private static void listen()
{
  //Run the Server inside a Thread
  server=new ExtendedThread()  //extends from Thread
  {
    public void run()
    {
      while( !isStopped()&&!isInterrupted() )
           {
             BufferedReader buffReader=null;

             try{ //Read a single line from the Socket
                  Socket socket=applLockSocket.accept();

                  buffReader=new BufferedReader( new InputStreamReader( socket.getInputStream() ) );

                  final String message=buffReader.readLine();

                  socket.close();

                  //Check the received message
                  if( ( message!=null&&message.length()>0 )&&GUI.display!=null&&!GUI.display.isDisposed() )  //激活某个GUI的代码在此,我们进去看看
                    {
                      GUI.display.asyncExec( new Runnable()
                      {
                        public void run()
                        {
                          //Restore the RSSOwl Window and handle Message
                          if( GUI.isAlive() )
                            {
                              GUI.rssOwlGui.restoreWindow();  //找到了,转到GUI.java

                              //Handle the message as Link if valid argument
                              if( RSSOwlLoader.isValidArgument( message ) )
                                {
                                  GUI.rssOwlGui.getEventManager().actionHandleSuppliedLink( message );
                                }
                            }
                        }
                      } );
                    }
                }
             catch( IOException e )
                  {
                    ...
                  }
             finally{ //关闭流
                    }
           }
    }
  };

  server.setDaemon( true );

  server.setName( "Startup Manager Thread" );

  server.start();
}
[/code]

追踪到 GUI.java 见下:

http://sourceforge.krugle.com/kse/files/cvs/cvs.sourceforge.net/rssowl/RSSOwl/src/java/net/sourceforge/rssowl/controller/GUI.java

追踪到GUI.restoreWindow()

[code]
/**
 * Restore the application window either from taskbar or the tray.
 */
public void restoreWindow()
{
  //RSSOwl is minimized to Tray
  if( GlobalSettings.useSystemTray()&&rssOwlSystray!=null&&rssOwlSystray.isMinimizedToTray() )
    {
      rssOwlSystray.restoreWindow();
    }
  else{ //RSSOwl is not active
        shell.forceActive();  //就是它了

        shell.setMinimized( false );
      }
}
[/code]

原理如下:

在本地8794端口建立服务监听,并且一直监听此端口,试图接受此端口其它实例(其它程序?)发送的输入

如果读到数据(好像没有判断数据的合法性,仅仅判断数据不为空),并且自己不是“激活”状态,就“激活”自己;

如果建立服务监听不成功,说明已经有实例(其它程序?)占用此端口了,向此端口发送数据唤醒前一个实例,停几秒以便前一个实例接受,然后退出。

程序用的是SWT,激活方法用的是shell.forceActive(),but it's not good for core java.

以上分析如有不对,恳请斧正。

 类似资料: