You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 09:50:13