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
相关产品推荐
相关产品推荐

