读取Firebase记录时ProgressBar不实时更新的解决办法咨询
问题描述
我用以下代码把Firebase记录保存到ArrayList中:
List<PoIs> LocoList = new ArrayList<>(); DatabaseReference ref = FirebaseDatabase.getInstance().getReference("DATABASE"); ProgressBar progressBar = dialog.findViewById(R.id.progressBar); progressBar.setProgress(0); ValueEventListener eventListener = new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { if (dataSnapshot.exists()) { int size = (int) dataSnapshot.getChildrenCount(); progressBar.setMax(size); for (DataSnapshot ds : dataSnapshot.getChildren()) { PoIs loco = ds.getValue(PoIs.class); LocoList.add(loco); progressBar.setProgress(LocoList.size()); // 省略后续代码 .......... } } } // 省略onCancelled方法 @Override public void onCancelled(DatabaseError databaseError) { // 处理错误逻辑 } }; ref.addValueEventListener(eventListener);
问题是我的ProgressBar只会在最后才更新,进度条一直停在0,十几秒后直接跳到3000。怎么才能让每次调用setProgress时都实时重绘进度条?
解决方案
核心原因是Firebase的onDataChange回调跑在主线程里,你写的for循环是同步执行的,会把主线程完全占住——主线程被阻塞的时候根本没时间更新UI,只能等整个循环跑完才会一次性刷新进度条。
给你两个实用的解决办法:
方法一:拆成小批次更新(简单易实现)
把数据处理拆成一小批一小批的,每次处理完就给主线程留时间刷新UI:
ValueEventListener eventListener = new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { if (dataSnapshot.exists()) { // 先把所有子节点存起来 List<DataSnapshot> childrenList = new ArrayList<>(); for (DataSnapshot ds : dataSnapshot.getChildren()) { childrenList.add(ds); } int totalSize = childrenList.size(); progressBar.setMax(totalSize); Handler mainHandler = new Handler(Looper.getMainLooper()); AtomicInteger currentCount = new AtomicInteger(0); Runnable processTask = new Runnable() { @Override public void run() { // 每次处理10条,可根据性能调整 int end = Math.min(currentCount.get() + 10, totalSize); for (int i = currentCount.get(); i < end; i++) { DataSnapshot ds = childrenList.get(i); PoIs loco = ds.getValue(PoIs.class); LocoList.add(loco); } currentCount.set(end); progressBar.setProgress(currentCount.get()); // 没处理完就继续循环 if (currentCount.get() < totalSize) { mainHandler.postDelayed(this, 10); // 留10ms给UI刷新 } } }; mainHandler.post(processTask); } } @Override public void onCancelled(DatabaseError databaseError) { // 处理错误逻辑 } };
方法二:子线程处理数据(更规范)
把数据解析的逻辑放到子线程,每次解析完一条就切回主线程更新进度:
ValueEventListener eventListener = new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { if (dataSnapshot.exists()) { int totalSize = (int) dataSnapshot.getChildrenCount(); progressBar.setMax(totalSize); // 开子线程处理数据 new Thread(() -> { int count = 0; for (DataSnapshot ds : dataSnapshot.getChildren()) { PoIs loco = ds.getValue(PoIs.class); LocoList.add(loco); count++; // 切回主线程更新进度条 runOnUiThread(() -> progressBar.setProgress(count)); // 可选:加个微小延时,避免UI更新太频繁卡顿 try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } } @Override public void onCancelled(DatabaseError databaseError) { // 处理错误逻辑 } };
注意点
- 如果是在Fragment里用,
runOnUiThread要改成getActivity().runOnUiThread(...) LocoList如果后续要在UI线程操作,记得保证线程安全,比如用CopyOnWriteArrayList或者加锁- 批次大小和延时时间可以根据你的数据量调整,找个流畅度和更新频率的平衡点
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

