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

Android:如何用AsyncTask复制选中文件并显示复制进度百分比

实现带百分比进度的文件复制(AsyncTask + ProgressDialog)

要实现复制进度的百分比显示,核心逻辑是先计算所有待复制文件的总大小,再在复制过程中实时统计已复制字节数,通过「已复制大小/总大小」计算进度百分比。结合你的代码,具体改造方案如下:

1. 先实现总文件大小计算

需要递归计算所有选中文件(包括子文件夹内的文件)的总大小,确保进度百分比准确:

// 计算单个文件/文件夹的总大小
private fun calculateTotalSize(file: File): Long {
    var totalSize = 0L
    if (file.isDirectory) {
        file.listFiles()?.forEach { childFile ->
            totalSize += calculateTotalSize(childFile)
        }
    } else {
        totalSize += file.length()
    }
    return totalSize
}

// 计算所有选中文件的总大小
private fun getSelectedTotalSize(selectedFiles: ArrayList<YourFileModel>): Long {
    var total = 0L
    selectedFiles.forEach { model ->
        if (model.check) {
            total += calculateTotalSize(model.file)
        }
    }
    return total
}

(注:YourFileModel替换成你代码中arrayListCopyMoveFile的实际元素类型)

2. 用AsyncTask封装复制任务

创建自定义AsyncTask,后台执行复制逻辑,实时更新ProgressDialog的进度:

class FileCopyTask(
    private val context: Context,
    private val selectedFiles: ArrayList<YourFileModel>,
    private val targetDir: String,
    private var totalSize: Long
) : AsyncTask<Void, Int, Boolean>() {

    private lateinit var progressDialog: ProgressDialog
    private var copiedSize = 0L

    override fun onPreExecute() {
        super.onPreExecute()
        // 初始化进度对话框
        progressDialog = ProgressDialog(context).apply {
            setTitle("正在复制文件")
            setMessage("已复制 0%")
            setProgressStyle(ProgressDialog.STYLE_HORIZONTAL)
            setMax(100)
            setCancelable(false)
            show()
        }
    }

    override fun doInBackground(vararg params: Void?): Boolean {
        return try {
            selectedFiles.forEach { model ->
                if (model.check) {
                    copyFileOrDirectoryWithProgress(model.file, File(targetDir))
                }
            }
            true
        } catch (e: Exception) {
            e.printStackTrace()
            false
        }
    }

    override fun onProgressUpdate(vararg values: Int?) {
        super.onProgressUpdate(*values)
        values[0]?.let { progress ->
            progressDialog.progress = progress
            progressDialog.setMessage("已复制 $progress%")
        }
    }

    override fun onPostExecute(result: Boolean?) {
        super.onPostExecute(result)
        progressDialog.dismiss()
        Toast.makeText(context, if (result == true) "复制完成" else "复制失败", Toast.LENGTH_SHORT).show()
    }

    // 带进度回调的递归复制方法
    private fun copyFileOrDirectoryWithProgress(srcFile: File, dstDir: File) {
        val dstFile = File(dstDir, srcFile.name)
        if (srcFile.isDirectory) {
            if (!dstFile.exists()) dstFile.mkdirs()
            srcFile.listFiles()?.forEach { childFile ->
                copyFileOrDirectoryWithProgress(childFile, dstFile)
            }
        } else {
            if (!dstFile.parentFile.exists()) dstFile.parentFile.mkdirs()
            copyFileWithProgress(srcFile, dstFile)
        }
    }

    // 带进度的单个文件复制(替换原copyFile方法)
    @Throws(IOException::class)
    private fun copyFileWithProgress(sourceFile: File, destFile: File) {
        val bufferSize = 8192 // 8KB缓冲区,平衡IO效率和进度更新频率
        val buffer = ByteArray(bufferSize)
        var bytesRead: Int

        FileInputStream(sourceFile).use { inputStream ->
            FileOutputStream(destFile).use { outputStream ->
                while (inputStream.read(buffer).also { bytesRead = it } != -1) {
                    outputStream.write(buffer, 0, bytesRead)
                    // 累加已复制大小并计算进度
                    copiedSize += bytesRead
                    val progress = ((copiedSize.toFloat() / totalSize) * 100).toInt()
                    publishProgress(progress)
                }
            }
        }
    }
}

3. 启动复制任务

替换你原来的复制循环,改为启动AsyncTask:

// 先计算所有选中文件的总大小
val totalSize = getSelectedTotalSize(arrayListCopyMoveFile)
// 启动复制任务
FileCopyTask(this, arrayListCopyMoveFile, path, totalSize).execute()

关键说明

  • 替换transferFrom原因:FileChannel.transferFrom是高效批量复制,但无法实时返回已复制字节数,必须改用逐缓冲区读写才能统计进度。
  • 缓冲区大小:8KB是通用平衡值,太小会增加IO次数,太大则进度更新不及时,可根据需求调整。
  • 递归处理:无论文件还是文件夹,都会递归计算大小和复制,确保进度百分比的准确性。

内容的提问来源于stack exchange,提问作者Xuân Đức Nguyễn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 23:01:07