有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

当使用RMI调用中的值修改其DefaultListModel时,JavaJList有时会变为空白

我见过很多这样的问题,但我仍然不明白我的实现出了什么问题

我正在尝试使用RMI构建一个客户机-服务器应用程序来实现通信;它应该成为一款游戏,但现在我只关注基本的客户端界面操作,比如登录、玩家列表等

在客户端,我有一个swing GUI,其中显示了服务器上当前注册的玩家列表。每次新玩家在服务器上注册时,服务器都会将注册玩家的完整列表发送给所有注册的客户端。然后,客户端必须清除其播放器列表内容,并用接收到的值再次填充

接收到的列表是IPlayer的数组,它是Player类导出的远程接口。要更新客户端上的JList,我使用下面的updatePlayerList()方法:

class BrowseScreen extends Screen implements ActionListener, MouseListener, ListSelectionListener {

private JList<String> playerList;

...

@Override
protected void init() {
    setLayout(new BorderLayout());

    ...

    playerList = new JList<String>();
    playerList.setModel(new DefaultListModel<String>());
    JScrollPane playerScroll = new JScrollPane(playerList);
    playerScroll.setBorder(BorderFactory.createTitledBorder(null, "Players", TitledBorder.CENTER, TitledBorder.DEFAULT_POSITION));
    add(playerList, BorderLayout.EAST);

    ...
}

public void updatePlayerList(IPlayer[] list) {
    try {
        DefaultListModel<String> model = (DefaultListModel<String>) playerList.getModel();

        model.clear();
        for (IPlayer p : list) {
            model.addElement(p.getName()); // remote method invocation
        }
        System.out.println(model); // added to check the content of the list model
    } catch (RemoteException e) {
        // TODO auto-generated catch block
        e.printStackTrace();
    }
}
...
}

(注意:Screen只是一个JPanel扩展,我用它来定义应用程序中的不同屏幕,比如登录、房间列表等。)

当我尝试打开多个客户机时,发生的情况是,在随机客户机上,列表完全变为空白,每次我使用其中一个客户机登录/退出时,JList的更新和其他一些(仍然是随机的)客户机可能会以空列表结束

列表模型应该用新数据正确更新,因为我在控制台上看到了正确的输出

我读了一些关于“JList不更新”问题的答案,最常见的答案是模型更新应该在EDT线程中执行。我不确定我做得是否正确,但我有一个主要问题:

public static void main(String[] args) throws RemoteException {
    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
            try {
                Client client = new Client();
                GuiClient gui = new GuiClient(client);
            } catch (RemoteException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    });
}

(注意:GuiClient对象使用对Client的引用来执行涉及与服务器交互的复杂操作。所有用户界面组件,如显示前一个ScreenJFrame,都由GuiClient管理)

我希望我能很好地解释情况(我对stackoverflow是新手);我的项目现在有很多课程,我不能在这里给出它的全貌

非常感谢您的帮助:)

编辑:使用SwingWorker执行RMI调用解决了问题

这就是我改变updatePlayerList()方法的方式:

public void updatePlayerList(IPlayer[] list) {
    new ReadPlayerList(list, playerList).execute();
}

这是ReadPlayerList类,它扩展了SwingWorker

public class ReadPlayerList extends SwingWorker<Void, String> {

private IPlayer[] list;
private JList<String> playerList;
private boolean listCleared;

public ReadPlayerList(IPlayer[] list, JList<String> playerList) {
    this.list = list;
    this.playerList = playerList;
    listCleared = false;
}

@Override
protected Void doInBackground() throws Exception {
    for (IPlayer p : list) {
        publish(p.getName());
    }
    return null;
}

@Override
protected void process(List<String> chunks) {
    DefaultListModel<String> model = (DefaultListModel<String>) playerList.getModel();

    if (!listCleared) {
        model.clear();
        listCleared = true;
    }

    for (String name : chunks) {
        model.addElement(name);
    }
}

}

这解决了JList无法显示其内容的问题。现在的挑战是确保这个更新操作始终是原子的。我想我必须编写某种“线程安全JList”扩展,但这是另一个故事:)


共 (1) 个答案

  1. # 1 楼答案

    您有一个涉及客户机-服务器通信的复杂系统,您会看到随机的、不可预测的错误。总之,你看到了线程问题的所有典型症状

    是的,您正在Swing线程上启动GUI,但这可能不是问题的根源。很可能(我不得不猜测),这是因为您没有使用RMI通信正确处理线程。你是不是在用一个SwingWorker来做这件事?如果我是你,我会这样做,这样你就可以将线程背景中的通信代码与Swing事件线程隔离开来,这样你就可以轻松地让通信代码与EDT上的Swing代码进行通信

    如果你需要更具体的帮助,你需要展示更多,最好是发布一个尽可能接近minimal example program的程序