为Firebase Storage文件下载添加OnProgressListener并修复进度异常
问题描述
我正在实现一个方法,通过OnProgressListener从Firebase Storage下载3D文件,并使用ProgressBar向用户展示下载进度百分比,但进度显示不符合预期,OnProgressListener内的代码未正常工作。以下是我的实现代码,希望能得到帮助修正使其正常运行:
private void download3dFile(String fileUrl){ downloadProgressLayout.setVisibility(View.VISIBLE); FirebaseStorage storage = FirebaseStorage.getInstance(); StorageReference httpReference = storage.getReferenceFromUrl(fileUrl); File localFile; try { localFile = File.createTempFile("model","glb"); } catch (IOException e) { throw new RuntimeException(e); } httpReference.getFile(localFile).addOnProgressListener(new OnProgressListener<FileDownloadTask.TaskSnapshot>() { @Override public void onProgress(@NonNull FileDownloadTask.TaskSnapshot taskSnapshot) { long percentage = taskSnapshot.getBytesTransferred()/taskSnapshot.getTotalByteCount()*100; progressPercentageTxt.setText(MessageFormat.format("{0}%", percentage)); progressBar.setProgress((int) percentage); } }).addOnCompleteListener(new OnCompleteListener<FileDownloadTask.TaskSnapshot>() { @Override public void onComplete(@NonNull Task<FileDownloadTask.TaskSnapshot> taskSnapshot) { Toast.makeText(MainActivity.this, "Download Complete", Toast.LENGTH_SHORT).show(); downloadProgressLayout.setVisibility(View.GONE); Uri fileUri = Uri.fromFile(localFile); Toast.makeText(MainActivity.this, "Uri - "+fileUri, Toast.LENGTH_LONG).show(); } }).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() { @Override public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) { } }).addOnCanceledListener(new OnCanceledListener() { @Override public void onCanceled() { Toast.makeText(MainActivity.this, "Download Canceled!", Toast.LENGTH_SHORT).show(); downloadProgressLayout.setVisibility(View.GONE); } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { Toast.makeText(MainActivity.this, "Download Failed - "+e.getMessage(), Toast.LENGTH_SHORT).show(); downloadProgressLayout.setVisibility(View.GONE); } }); }
修正方案
核心问题1:整数除法导致进度计算错误
getBytesTransferred()和getTotalByteCount()都是long类型,直接执行除法会触发整数除法——只有当已传输字节数达到总字节数的整数倍时,结果才会非零,这会导致进度一直显示0%,直到下载完成直接跳转到100%。
修复方式:先将其中一个值转为浮点型再计算,确保得到精确的百分比:
// 转浮点型避免整数除法,再取整用于进度显示 double percentage = (double) taskSnapshot.getBytesTransferred() / taskSnapshot.getTotalByteCount() * 100; int progress = (int) Math.round(percentage);
核心问题2:局部变量作用域风险
localFile在try块内初始化,若try块执行失败,后续addOnCompleteListener中访问localFile会出现空指针;同时部分Java版本会限制跨代码块访问局部变量。
修复方式:提前声明变量并处理异常终止逻辑:
File localFile = null; try { localFile = File.createTempFile("model","glb"); } catch (IOException e) { e.printStackTrace(); Toast.makeText(MainActivity.this, "创建临时文件失败", Toast.LENGTH_SHORT).show(); downloadProgressLayout.setVisibility(View.GONE); return; // 终止方法,避免后续空指针 }
优化点:UI线程安全性
虽然Firebase监听器默认在主线程执行,但显式指定主线程更新UI可以避免潜在的线程问题:
runOnUiThread(() -> { progressPercentageTxt.setText(String.format("%d%%", progress)); progressBar.setProgress(progress); });
完整修正代码
private void download3dFile(String fileUrl){ downloadProgressLayout.setVisibility(View.VISIBLE); FirebaseStorage storage = FirebaseStorage.getInstance(); StorageReference httpReference = storage.getReferenceFromUrl(fileUrl); File localFile = null; try { localFile = File.createTempFile("model","glb"); } catch (IOException e) { e.printStackTrace(); Toast.makeText(MainActivity.this, "创建临时文件失败", Toast.LENGTH_SHORT).show(); downloadProgressLayout.setVisibility(View.GONE); return; } httpReference.getFile(localFile).addOnProgressListener(new OnProgressListener<FileDownloadTask.TaskSnapshot>() { @Override public void onProgress(@NonNull FileDownloadTask.TaskSnapshot taskSnapshot) { // 修正整数除法问题 double percentage = (double) taskSnapshot.getBytesTransferred() / taskSnapshot.getTotalByteCount() * 100; int progress = (int) Math.round(percentage); // 确保UI操作在主线程执行 runOnUiThread(() -> { progressPercentageTxt.setText(String.format("%d%%", progress)); progressBar.setProgress(progress); }); } }).addOnCompleteListener(new OnCompleteListener<FileDownloadTask.TaskSnapshot>() { @Override public void onComplete(@NonNull Task<FileDownloadTask.TaskSnapshot> taskSnapshot) { downloadProgressLayout.setVisibility(View.GONE); if (taskSnapshot.isSuccessful() && localFile != null) { Toast.makeText(MainActivity.this, "下载完成", Toast.LENGTH_SHORT).show(); Uri fileUri = Uri.fromFile(localFile); Toast.makeText(MainActivity.this, "Uri - "+fileUri, Toast.LENGTH_LONG).show(); } else { Toast.makeText(MainActivity.this, "下载未完成", Toast.LENGTH_SHORT).show(); } } }).addOnCanceledListener(new OnCanceledListener() { @Override public void onCanceled() { Toast.makeText(MainActivity.this, "下载已取消!", Toast.LENGTH_SHORT).show(); downloadProgressLayout.setVisibility(View.GONE); } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { Toast.makeText(MainActivity.this, "下载失败 - "+e.getMessage(), Toast.LENGTH_SHORT).show(); downloadProgressLayout.setVisibility(View.GONE); } }); }
额外优化说明
- 移除了空实现的
addOnSuccessListener,addOnCompleteListener已可处理成功场景 - 在
onComplete中增加了任务成功状态和localFile非空判断,避免空指针 - 异常处理中增加用户提示并终止方法,防止后续逻辑出错
内容的提问来源于stack exchange,提问作者Agasthi
相关产品推荐
相关产品推荐

