Async配合RecyclerView加载数据时如何边加载边逐条追加显示列表
问题根因定位
- 回调线程问题:首先排查
deviceWearableService.getBuilding的成功回调是否执行在子线程。Android要求所有UI更新操作必须在主线程执行,如果你在子线程调用notifyItemInserted,系统不会立刻触发RecyclerView重绘,会把所有更新事件攒到主线程下一次批量处理,就会出现所有数据加载完成后统一渲染的现象。 - 初始化顺序问题:你的代码先触发所有
fetchBuildingAddress请求,之后才给RecyclerView设置adapter和layoutManager。如果部分请求返回速度快于RecyclerView初始化流程,前几个返回数据的插入通知不会被还没绑定的RecyclerView接收,等RecyclerView初始化完成后才会一次性处理所有攒下的插入事件,也会导致批量渲染。
修复方案
1. 先完成RecyclerView初始化再发起请求
调整onCreateView里的代码顺序,先给RecyclerView设置好布局管理器和适配器,再发起批量请求:
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val view = inflater.inflate(R.layout.fragment_lock_list, container, false) val recyclerView = view.findViewById<RecyclerView>(R.id.list) // 先初始化RecyclerView with(recyclerView) { layoutManager = LinearLayoutManager(context) adapter = deviceRecyclerViewAdapter } // 再发起请求 for(device in devices) { fetchBuildingAddress(device) } return view }
2. 强制UI更新操作切到主线程执行
在插入数据前先判断当前线程,确保notifyItemInserted在主线程调用,修改fetchBuildingAddress的回调逻辑:
private fun fetchBuildingAddress(device: Device) { deviceWearableService.getBuilding(device.buildingId) { when(it) { is DeviceWearableServiceImpl.State.Success -> { val buildingName = it.resources.buildingAddress.address1 val deviceBuilding = DeviceBuillding(device, buildingName) // 切主线程执行插入更新 Handler(Looper.getMainLooper()).post { deviceRecyclerViewAdapter.insertDevice(deviceBuilding) } } else -> { // TODO: ERROR STATE } } } }
3. 可选优化方案
如果你的请求可以控制并发,也可以改用串行请求的方式,加载完一条再发起下一条请求,更贴合逐条加载的交互预期:
// 把请求列表转成迭代器逐次调用 private lateinit var deviceIterator: Iterator<Device> override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val view = inflater.inflate(R.layout.fragment_lock_list, container, false) val recyclerView = view.findViewById<RecyclerView>(R.id.list) with(recyclerView) { layoutManager = LinearLayoutManager(context) adapter = deviceRecyclerViewAdapter } deviceIterator = devices.iterator() fetchNextDevice() return view } private fun fetchNextDevice() { if (deviceIterator.hasNext()) { val device = deviceIterator.next() deviceWearableService.getBuilding(device.buildingId) { when(it) { is DeviceWearableServiceImpl.State.Success -> { val buildingName = it.resources.buildingAddress.address1 val deviceBuilding = DeviceBuillding(device, buildingName) Handler(Looper.getMainLooper()).post { deviceRecyclerViewAdapter.insertDevice(deviceBuilding) } } } // 不管成功失败都加载下一条 fetchNextDevice() } } }
内容的提问来源于stack exchange,提问作者mengmeng
相关产品推荐
相关产品推荐

