在从单独的线程通知Observer update()之后,Java swing GUI组件未显示

时间:2012-04-19 06:46:35

标签: java multithreading swing

我的应用程序基本上使用apache的httpclient连接到服务器并为用户做些事情。

我是一名java初学者(虽然不是开发初学者),但许多特定于Java的方法可能会被冷静地忽略。

描述

  • 该应用程序使用带有弹出菜单的TrayIcon作为唯一的用户界面,并对托盘消息或弹出[确认]对话框作出反应。
  • 它使用线程,因为httpclient在等待响应时有效地阻塞了主线程
  • 使用客户端 - 服务器仅确保单个实例并对第二次实例尝试做出反应
  • 使用observer / observable模式对httpclient和单实例监视程序服务器的消息作出反应
  • 主要行为由 Controller 类控制,该类是唯一的Observer;与httpclient相关的所有内容都在一个分离的 WebClient 类中,它实现了Runnable并扩展了Observable;然后是 AttServer ,它再次是Runnable和Observable,并控制服务器监听器周围的一切(实际上并不多)。

这是我在Java中的第一个应用程序,因此可能存在许多晦涩难懂...... :(

问题

所需的行为是,每当用户尝试运行此应用程序的第二个实例时,它会尝试在端口X上绑定套接字侦听器,并且失败时会向该端口发送字符串消息。主要' app接受消息并对显示的确认对话框做出反应,要求用户输入一些信息。

当我添加客户端服务器时,一切正常工作;第二个实例成功触发观察者的update()方法,观察者正确区分正在进行的操作并触发相应的方法;然后在第一个新的GUI组件(确认对话框)应该出现时,没有发生:

  • 应用程序没有做任何事情
  • System.out
  • 中没有抛出任何错误或异常
  • 托盘图标CONTINUES工作正常(右键单击显示菜单等)

..只是对话后的所有说明都没有执行,对话框也没有出现。

WEIRDNESS#1

当用户使用托盘图标菜单触发显示对话框的其他命令时,GUI以某种方式咳嗽并以正确的顺序显示两个(或更多适当的时候)对话框(首先显示卡住的隐藏对话框然后第二个新鲜的)并且一切似乎都是正确的状态并且所有命令都被正确执行(尽管其中一些命令稍后有点)。

WEIRDNESS#2

我猜这是因为挥杆与线程不兼容;但是,当update()方法从httpclient(也是一个单独的线程)中启动时,GUI工作正常。观察者获得通知的方式的唯一区别是 WebClient 在一些用户操作之后调用notifyObservers()(虽然在一个单独的线程中以及在(最差的)30s超时和 AttServer之后在catch()块中调用notifyObservers(),因为当指定的端口已被绑定时抛出IOException。

WEIRDNESS#3

这个问题至少可以说是不规则的。它通常发生在开发环境(netbeans)中,虽然有时候它并没有 - 起初我得到的印象是它通过调用另一个(虚拟)JDialog来解决,它仍然是故意隐藏的,实际上有问题的对话 - 看起来摇摆需要一点点“推开门”#;;虽然在将项目构建到jar或exe(通过launch4j)后,问题又回来了。然后我尝试在所有的swing对话框中使用单个JFrame组件,它有所帮助 - 有时当输出jar运行时它会正常工作,第二天它会再次隐藏对话框。

我完全迷失了,在地球上搜寻解决方案,没有发现任何真的。我希望其他人可能碰到一些类似的问题,并且作为一个更有经验的Javaman,找到了解决方案。我还在主JFrame实例上尝试了一些doLayout(),validate(),revalidate(),repaint(),update()调用,但没有去。

有希望:)感谢阅读,并感谢任何想法。

代码示例

这是来自 AttServer 的run()函数 - 非工作更新()的来源...(它有点硬编码,因为主要功能在那里,我被困在其他地方..)

public void run() {

    this.addObserver( Controller.getInstance() );

    setChanged();

    // this.command == INIT set and new thread with AttServer called in the beginning of the main()
    if( null != this.command ) {

        switch( this.command ) {
            case INIT:
                try {
                    init();
                } catch( IOException ex ) {
                    notifyObservers( new ErrorEvent( 100 , "Could not bind the specified port." ) );
                }
            break;
            default:
        } //switch

    // no command, looks like an incoming connection
    } else {

        try {
            DataInputStream in = new DataInputStream( socket.getInputStream() );

            String inputLine;

            while( ( inputLine = in.readLine() ) != null ) {

                if( inputLine.trim().equals( EventType.CLOCKIN.toString() ) ) {

                    notifyObservers( EventType.CLOCKIN );

                    return;
                }
            }
        } catch( IOException ex ) {
            //todo
        }
    }
}

..这是围栏的另一面,即update()方法:

public void update( Observable obj, Object arg) {
    // received a remote request *******************************************
    if( obj instanceof AttServer
        && arg instanceof EventType ) {

        EventType command = (EventType) arg;

        Attendance.debugMsg( "Received " + command.toString() + " request from AttServer" );

        if( false == isOnClock ) {
            doClock( true , true );
        }
    } // ..........

..这是从update()调用的doClock()方法的本质 - 实际上并没有什么,即使在调用之后立即显示对话框,或者在事件发生时也是如此。如果在update()方法中显示,它不起作用。

(overloaded with default data)
public static void doClock( Boolean clockIn , Boolean confirm , String message , String title ) {

    Event lastEvent = Log.getLastClock();

    if( confirm ) {
        if( JOptionPane.NO_OPTION == JOptionPane.showConfirmDialog( mainFrame , message , title , JOptionPane.YES_NO_OPTION ) ) {
            return;
        }
    }

1 个答案:

答案 0 :(得分:5)

如果没有阅读整个文档(!!!),我猜你不会通过Event Dispatch Thread (EDT)更新GUI。 GUI应仅由单个线程,EDT 更新。
您可以在应用程序中使用后台线程,但必须将更新GUI传递给EDT 。 请勿触摸您自己的线程中的任何Swing控件,即EDT以外的其他线程 否则你会遇到很多问题 不通过EDT显然是错误的