2

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

我是一个java初学者(虽然不是一个开发初学者),所以很多java特定的方法可能已经被冷静地忽略了。

描述

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

这是我在 Java 中的第一个应用程序,可能有很多晦涩难懂的地方..:(

问题

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

在我添加客户端服务器之前,一切都运行良好;第二个实例成功触发了观察者的update()方法,观察者正确分辨出发生了什么并触发相应的方法;然后在出现第一个新的 GUI 组件(确认对话框)时,什么都没有发生:

  • 该应用程序不执行任何操作
  • System.out 中没有抛出错误或异常
  • 托盘图标继续正常工作(右键单击显示菜单等)

..只是对话框之后的所有指令都没有执行,对话框也没有出现。

怪异#1

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

怪异 #2

我猜这是由于摆动与线程不兼容造成的;但是,当 update() 方法从 httpclient(也是一个单独的线程)启动时,GUI 工作得很好。Observer 获得通知方式的唯一区别是WebClient在某些用户操作之后调用 notifyObservers()(尽管在单独的线程中并且在(最坏的情况下)30 秒超时之后,并且AttServer在 catch() 块中调用 notifyObservers() ,因为当指定的端口已经绑定时会抛出 IOException。

奇怪#3

这个问题至少可以说是不规则的。它通常发生在开发环境(netbeans)中,虽然有时它不会——起初我的印象是通过调用另一个(虚拟)JDialog来解决它,在实际有问题的对话框之前故意隐藏它—— ——似乎秋千需要一点“推门”;尽管在将项目构建成 jar 或 exe(通过 launch4j)后,问题又回来了。然后我尝试对所有的摆动对话框使用单个 JFrame 组件,它以某种方式提供了帮助 --- 有时当输出 jar 运行时它会正常工作,第二天它再次隐藏对话框。

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

有希望 :) 感谢您的阅读,更感谢您的任何想法。

代码示例

这是来自AttServer的 run() 函数——不工作的 update()s 的来源 ..(它有点硬编码,因为主要功能在那里,我在其他地方卡住了..)

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;
        }
    }
4

1 回答 1

5

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

于 2012-04-19T07:00:35.403 回答