前几天自己发表的只运行一个实例的文章,感谢Pande的留意,并提出宝贵意见,他推荐的正好是我想找的RSSOwl源码,以下是我分析RSSOwl如何做到只运行一个实例并且激活的,如有错误,请拍砖。
根据 RSSOwlLoader.java 中的
[code]
private static void startupProcess( String[] args )
{
...
if( !System.getProperties().containsKey( PROPERTY_ALLOW_MULTI_INSTANCES )&&StartupManager.isRSSOwlRunning( feedArgument ) )
...
}
[/code]
追踪到 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 见下:
追踪到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.
以上分析如有不对,恳请斧正。