【问题标题】:GUI not updating as expected/control flow issueGUI 未按预期更新/控制流问题
【发布时间】:2013-05-22 01:40:35
【问题描述】:

我正在为一个学校项目制作一款联网战舰游戏。 GUI 有一个JButtons 网格,用作游戏板。当用户点击一个按钮时,它会调用下面的sendShot() 方法。此方法将他们的镜头发送到远程机器,远程机器检查它是命中还是未命中并返回结果。然后第一台机器接收该结果并相应地更新其 GUI。我在下面的代码正在执行此操作,但我的问题是直到另一台机器发送下一个镜头之后,GUI 才会更新。我猜这是因为我在sendShot() 方法中调用recieveShot(),但我不是100% 清楚为什么,因为更新GUI 的方法(gp.ob.updateBoard(sr)))是在@987654327 之前调用的@。我在这里有什么误解?

此外,我感觉我这里的基本程序流方法有缺陷,不应该从sendShot() 方法中调用receiveShot()。这个方案有什么明显的替代方案吗?

void sendShot(ShotAttempt sa){

    try {
       oos.writeObject(sa);
       oos.flush();
       System.out.println("shot fired");
       ShotResult sr = (ShotResult)ois.readObject();
       gp.ob.updateBoard(sr); 
    } catch (IOException | ClassNotFoundException e) {
        System.out.println(e.printStackTrace());}
      receiveShot();
}

void receiveShot(){
    try{
    ShotAttempt sa = (ShotAttempt)ois.readObject();
    ShotResult sr = gp.db.acceptShot(sa);
    oos.writeObject(sr);
    oos.flush();
    } catch (IOException | ClassNotFoundException e){e.printStackTrace();}
}

【问题讨论】:

  • 这可能与它没有任何关系,但您从流中读取了两次ShotResult...一次在sendShot 中,再次在recieveShot 中。此外,我们可能需要查看更多代码才能推断出整体问题......
  • 能否请您粘贴SSCCE 以获得快速帮助。

标签: java swing sockets concurrency event-dispatch-thread


【解决方案1】:

直到另一台机器发送下一个镜头后,GUI 才会更新。我猜这是因为我在 sendShot() 方法中调用了 recieveShot(),但我不是 100% 清楚为什么,因为更新 GUI 的方法 (gp.ob.updateBoard(sr)) ) 在 receiveShot() 之前调用。

receiveShot() 方法阻塞 Swing 事件调度线程或 EDT。由于该线程负责所有 Swing 图形、用户交互和 Swing 事件处理,因此不会处理在事件线程上排队和等待处理的 Runnable,即使它们在调用 receiveShot() 方法之前已排队。它们只能在线程被解除阻塞时进行处理,这将是在你收到一个镜头之后。

建议:

  • 您应该在后台线程上进行所有套接字通信。
  • 我会创建程序状态,可能由一个枚举持有,比如称为 State,并给它至少两个可能的值,SHOOTING 和 RECEIVING。
  • 在发送镜头后适当地设置您的状态为 State.RECEIVING。
  • 接球后设置回 State.SHOOTING。
  • 不允许玩家在接球状态下投篮。这样,您的代码将不需要阻止方法来阻止用户射击。
  • 您还可以将 END_GAME 作为您的状态之一。
  • 我会给我的班级一个setState(State state) 方法。
  • 在这种方法中,我可以根据新状态启用/禁用我的拍摄按钮。
  • 您需要一个后台线程,可能是 SwingWorker,持续运行,监听套接字的消息。当这个线程接收到它已经被射击的信息时,它会通过调用setState(newState)来更新程序的状态。
  • 我会注意在 Swing 事件线程上调用 setState,以便状态更改全部由一个线程完成。
  • 然后我会在一个单独的后台线程上调用拍摄,但它只存在用于拍摄。它不会持续运行。

有关 Swing 事件调度线程的更多信息,请查看Concurrency in Swing

【讨论】:

  • 谢谢!我正试图围绕你的建议来思考:那么什么时候会检查状态?
  • @user2356560:当用户尝试射击时,当他按下按钮时,您可以检查状态。如果状态不是 State.SHOOTING,则按下按钮不会执行任何操作。或者,您可以监听状态的变化,如果状态不是 State.SHOOTING,您可以通过 setEnabled(false) 禁用按钮,反之亦然,当状态变回时。
  • "Alternatively, you could listen for the State to change" 如何监听状态变化?我可以使用什么构造?
  • 这里是不是在讨论并发/同步,因为这是我刚刚熟悉的话题……
  • @user2356560:将涉及并发原则。我认为您不需要使用任何同步块,但需要注意所有 Swing 代码都在 Swing 线程上完成。再次,请查看我的答案底部的链接。
猜你喜欢
  • 2017-05-17
  • 2021-11-22
  • 2018-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多