Android:调用notifyDataSetChanged()后RecyclerView需点击EditText才更新
问题根因
- 出现该现象的核心原因是 在子线程中直接调用RecyclerView Adapter的
notifyDataSetChanged()方法 - Android存在严格的UI操作线程约束:所有和视图绘制、布局更新相关的触发逻辑,必须在主线程(UI线程)执行才会被立即调度。与原生View的setText、setVisibility等方法自带线程检查不同,RecyclerView的Adapter的notify类方法没有强制校验调用线程,在子线程发起的数据集变更通知不会被立即触发重绘流程,只会等待下一次主线程处理到布局绘制任务时才生效
- 你的代码中将SOAP网络请求、数据解析逻辑全部放在独立的
SOAPCONNECTRunnable(子线程)中执行,子线程内调用listSPAdapter.notifyDataSetChanged()时,不会立即触发RecyclerView的重绘调度;当你触碰EditText时,输入框的焦点变化、触摸反馈触发了一次主线程的布局重绘流程,才顺带读取到最新的数据集完成刷新,也就出现了“发起请求后不立即更新,点一下输入框才显示数据”的现象。
修复方案
1. 将数据集更新、Adapter刷新逻辑切回主线程执行
在SOAP请求完成、数据解析结束后,必须把刷新操作投递到主线程执行,确保notify方法触发的重绘请求能被立即调度。可以通过以下两种常用方式实现:
- 调用当前Activity的
runOnUiThread()方法,把更新逻辑包裹在主线程Runnable中 - 通过绑定主线程的Handler发送空消息,在消息处理回调中执行更新
注意:不要在子线程内直接操作Adapter实例或调用任何与视图更新相关的方法
2. 修复现有逻辑的已知隐患
- 每次发起新请求前,先调用
arrSanPham.clear()清空旧数据集,避免多次请求时新旧数据重复叠加 - 优化异常处理逻辑,请求失败、数据解析出错时给用户明确的界面提示,不要只打印后台日志
核心代码修改示例
原错误的子线程内更新写法:
// 运行在子线程的run()方法内 for (int i = 0;i<tongSP;i++){ // ... 数据解析逻辑 arrSanPham.add(new SanPham(...)); } listSPAdapter.notifyDataSetChanged();
修改后的正确实现:
@Override public void run() { // 原有SOAP请求、网络交互的子线程逻辑保持不变 try{ ht.call(SOAP_ACTION,envelope); // ... 原有SOAP数据解析逻辑 int tongSP = Sanpham.getPropertyCount(); // 把UI更新逻辑投递到主线程执行 runOnUiThread(new Runnable() { @Override public void run() { // 先清空旧数据,避免重复叠加 arrSanPham.clear(); // 解析并添加本次的新数据 for (int i = 0;i<tongSP;i++){ SoapObject Sanpham1 = (SoapObject) Sanpham.getProperty(i); arrSanPham.add(new SanPham( Integer.parseInt(Sanpham1.getProperty("MaSanPham").toString()), Integer.parseInt(Sanpham1.getProperty("SoLuong").toString()), Sanpham1.getProperty("TenSanPham").toString() )); } // 主线程内调用刷新,会立即触发重绘 listSPAdapter.notifyDataSetChanged(); } }); }catch(Exception e){ Log.d("Error","Error while recieving data"); // 失败提示也需要切到主线程显示 runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(YourActivity.this, "请求失败,请稍后重试", Toast.LENGTH_SHORT).show(); } }); } }
长期优化建议
- 将
SOAPCONNECT改为静态内部类,避免隐式持有外部Activity引用造成内存泄漏 - 网络请求过程中增加全局/局部加载提示,避免用户重复点击发起无效请求
- 替换全量刷新的
notifyDataSetChanged()方法,改用notifyItemRangeInserted()做定向刷新,减少列表的整体重绘开销,提升滑动流畅度
内容的提问来源于stack exchange,提问作者HoangGiaMinh
相关产品推荐
相关产品推荐

