Vaadin UI集成Retrofit异步请求后修改UI报错如何解决?
错误原因
Vaadin 对 UI 组件、会话状态的修改做了线程安全限制,所有涉及 UI 变更、会话状态读写的操作必须持有对应 VaadinSession 或 UI 实例的锁才能执行。Retrofit 的异步回调方法 onResponse/onFailure 默认运行在 Retrofit 管理的后台线程中,没有持有 Vaadin 的会话锁,直接操作 UI 就会抛出该错误。
解决方案
核心是把所有修改UI的逻辑,包装在UI.access()或者VaadinSession.access()方法中,由Vaadin自动处理锁的获取和释放,代码示例和注意事项如下:
- 方案1:使用
UI.access()(适用绝大多数单UI修改场景)
发起Retrofit请求前先保存当前请求关联的UI实例,回调中把UI修改逻辑包裹在access方法内:// 发起请求前在Vaadin主线程中获取当前UI实例 UI currentUI = UI.getCurrent(); // 发起Retrofit异步请求 yourApiService.getYourData().enqueue(new Callback<YourResponseClass>() { @Override public void onResponse(Call<YourResponseClass> call, Response<YourResponseClass> response) { // 先处理非UI的业务逻辑,不要放到access块里避免阻塞UI线程 String showText; if (response.isSuccessful()) { showText = "请求成功:" + response.body().getContent(); } else { showText = "请求失败,错误码:" + response.code(); } // 仅把UI修改逻辑放入access块 currentUI.access(() -> { // 此处可直接操作任意UI组件 resultLabel.setText(showText); dataGrid.setItems(response.body().getDataList()); }); } @Override public void onFailure(Call<YourResponseClass> call, Throwable t) { currentUI.access(() -> { Notification.show("请求出错:" + t.getMessage()); }); } }); - 方案2:使用
VaadinSession.access()(适用会话级状态修改、多UI共享场景)
如果你需要修改的是会话层面的共享数据,或者场景下无法获取到单个UI实例,可以改用会话级的access方法,用法和UI级完全一致,只需提前保存VaadinSession.getCurrent()实例即可。 - 注意事项
access方法会自动判断UI/会话是否存活,如果用户已经关闭页面,内部逻辑会自动跳过不会抛出异常,无需额外做判空处理- 不要在
access块中放入耗时的IO、计算逻辑,该块内的代码运行在Vaadin UI线程,阻塞会影响所有同会话的UI操作 - 如果项目开启了服务端推送(添加了
@Push注解),access方法执行完成后会自动将UI变更推送到前端,无需手动调用push()方法
内容的提问来源于stack exchange,提问作者user17396184
相关产品推荐
相关产品推荐

