Android中AsyncTask结合Firebase上传SMS时进度显示异常问题
问题根源分析
你遇到的核心问题是:Firebase的setValue()是异步操作——你的doInBackground方法只是快速把所有上传请求发送给了Firebase服务器,但并没有等待请求真正完成,就立刻执行了i++和publishProgress(i)。这就导致进度条瞬间走完,但Firebase后台还在逐个处理这些异步请求,所以控制台里的消息会慢慢出现。
解决方案:等待每个上传任务完成后再更新进度
要获取真实的上传进度,我们需要确保只有当Firebase确认某个消息上传成功后,才更新进度计数。下面是基于你的代码修改后的实现:
步骤1:先把所有消息数据读取到本地列表
先在主线程把游标里的所有消息数据读取到一个列表中,避免在后台线程操作游标引发线程安全问题:
// 定义一个数据类保存消息字段 private static class MessageData { long id; String type; String message; String formattedTime; long timestamp; public MessageData(long id, String type, String message, String formattedTime, long timestamp) { this.id = id; this.type = type; this.message = message; this.formattedTime = formattedTime; this.timestamp = timestamp; } } // 读取所有消息到列表 List<MessageData> messageList = new ArrayList<>(); while (cur.moveToNext()) { long _id = cur.getLong(cur.getColumnIndex("_id")); String type = cur.getString(cur.getColumnIndex("type")); String body = cur.getString(cur.getColumnIndex("body")); long timestamp = cur.getLong(cur.getColumnIndex("date")); String formattedTime = dateFormat.format(timestamp); messageList.add(new MessageData(_id, type, body, formattedTime, timestamp)); } cur.close(); totalMsgs = messageList.size();
步骤2:修改AsyncTask,等待每个上传任务完成
使用CountDownLatch让后台线程等待当前消息的上传请求完成后,再处理下一个消息,确保进度更新的时机是真实的上传完成时刻:
private class UploadConversations extends AsyncTask<Void, Integer, Integer> { private List<MessageData> mMessageList; private DatabaseReference mRootRef; public UploadConversations(List<MessageData> messageList) { mMessageList = messageList; mRootRef = FirebaseDatabase.getInstance().getReference(); } @Override protected void onPreExecute() { super.onPreExecute(); mBuilder.setProgress(totalMsgs, 0, false); mNotifyManager.notify(id, mBuilder.build()); } @Override protected Integer doInBackground(Void... params) { int uploadedCount = 0; for (MessageData msg : mMessageList) { // 处理任务取消情况 if (isCancelled()) break; CountDownLatch uploadLatch = new CountDownLatch(1); String msgID = String.valueOf(msg.id); DatabaseReference msgRef = mRootRef.child("Chats").child(userID).child(thread_id).child(msgID); // 用Map批量设置字段,减少网络请求次数 Map<String, Object> msgMap = new HashMap<>(); msgMap.put("_id", msg.id); msgMap.put("type", msg.type); msgMap.put("message", msg.message); msgMap.put("time", msg.formattedTime); msgMap.put("time_stamp", msg.timestamp); // 上传并监听完成状态 msgRef.setValue(msgMap) .addOnCompleteListener(task -> { if (task.isSuccessful()) { uploadedCount++; publishProgress(uploadedCount); } else { Log.e(LOGTAG, "上传消息失败:" + msg.id, task.getException()); } // 不管成功失败,都释放锁,继续下一个消息 uploadLatch.countDown(); }); try { // 等待当前上传任务完成 uploadLatch.await(); } catch (InterruptedException e) { Log.e(LOGTAG, "上传被中断", e); Thread.currentThread().interrupt(); break; } } return uploadedCount; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); mBuilder.setProgress(totalMsgs, values[0], false); mNotifyManager.notify(id, mBuilder.build()); } @Override protected void onPostExecute(Integer result) { super.onPostExecute(result); mBuilder.setContentTitle("上传完成"); mBuilder.setContentText("已备份 " + result + "/" + totalMsgs + " 条消息"); mBuilder.setProgress(0, 0, false); mNotifyManager.notify(id, mBuilder.build()); mNotifyManager.cancel(id); Toast.makeText(getApplicationContext(), "备份完成:共上传" + result + "条消息", Toast.LENGTH_LONG).show(); } @Override protected void onCancelled() { super.onCancelled(); mBuilder.setContentTitle("上传取消"); mBuilder.setContentText("备份已停止"); mBuilder.setProgress(0, 0, false); mNotifyManager.notify(id, mBuilder.build()); mNotifyManager.cancel(id); Toast.makeText(getApplicationContext(), "备份已取消", Toast.LENGTH_LONG).show(); } }
步骤3:启动AsyncTask
new UploadConversations(messageList).execute();
额外优化建议
- 分批次并行上传:如果消息数量很大,串行上传会比较慢,可以考虑每次同时上传3-5条消息,等这一批全部完成后再更新进度,既保证进度准确,又提升上传速度。
- 替换AsyncTask:AsyncTask已经在Android 11被废弃,建议改用
Coroutines(协程)或WorkManager来处理这类后台任务,它们的生命周期管理和线程调度更可靠。
内容的提问来源于stack exchange,提问作者ALi Akbar
相关产品推荐
相关产品推荐

