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

Android:如何通过对话框取消文件下载并删除文件

解决Android下载任务取消后无法终止进程及删除文件的问题

我看了你的代码,问题主要出在没有在下载过程中检查任务是否被取消,以及取消后没有清理已下载的文件。下面一步步帮你修复:

问题分析

  1. 你调用了DownloadingTask.this.cancel(true),但doInBackground里没有检查isCancelled(),所以下载循环会一直跑直到完成。
  2. 取消任务后没有删除已经下载了一部分的文件,导致残留垃圾。
  3. 用runOnUiThread更新进度不是AsyncTask的标准用法,应该用onProgressUpdate方法。
  4. 上下文引用有点问题,比如bookejtemaeat.this在独立的DownloadTask类里可能导致内存泄漏,应该用传入的context。

修改后的完整代码

public class DownloadTask {
    private static final String TAG = "Download Task";
    private Context context;
    private String downloadUrl = "", downloadFileName = "";
    private ProgressDialog progressDialog;
    private DownloadingTask downloadingTask;
    private File outputFile; // 把outputFile提升为成员变量,方便取消时删除

    public DownloadTask(Context context, String downloadUrl) {
        this.context = context;
        this.downloadUrl = downloadUrl;
        downloadFileName = downloadUrl.substring(downloadUrl.lastIndexOf('/'), downloadUrl.length());
        Log.e(TAG, downloadFileName);
        // 初始化并启动下载任务
        downloadingTask = new DownloadingTask();
        downloadingTask.execute();
    }

    private class DownloadingTask extends AsyncTask<Void, Integer, Void> {
        File apkStorage = null;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            progressDialog = new ProgressDialog(context); // 替换成传入的context,避免内存泄漏
            progressDialog.setMessage("يتم تحميل الملف مرة واحدة يرجى الانتظار ......");
            progressDialog.setIndeterminate(false); // 因为用了水平进度条,设为false更合理
            progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            progressDialog.setCancelable(false);
            progressDialog.setProgress(0);
            progressDialog.setCanceledOnTouchOutside(false);
            progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    downloadingTask.cancel(true); // 触发任务取消
                    dialog.dismiss();
                }
            });
            progressDialog.show();
        }

        @Override
        protected Void doInBackground(Void... arg0) {
            try {
                URL url = new URL(downloadUrl);
                HttpURLConnection c = (HttpURLConnection) url.openConnection();
                c.setRequestMethod("GET");
                c.connect();

                final int fileLength = c.getContentLength();
                Log.e(TAG, "fileLength " + fileLength);

                if (c.getResponseCode() != HttpURLConnection.HTTP_OK) {
                    Log.e(TAG, "Server returned HTTP " + c.getResponseCode() + " " + c.getResponseMessage());
                    return null;
                }

                // 检查SD卡或使用私有目录
                if (new CheckForSDCard().isSDCardPresent()) {
                    apkStorage = context.getApplicationContext().getDir("NKDROID FILES", Context.MODE_PRIVATE);
                } else {
                    // 这里可以考虑使用内部存储,而不是只弹Toast
                    apkStorage = context.getFilesDir();
                    Toast.makeText(context, "Tidak ada SD Card, menggunakan penyimpanan internal.", Toast.LENGTH_SHORT).show();
                }

                if (!apkStorage.exists()) {
                    apkStorage.mkdir();
                    Log.e(TAG, "Directory Created.");
                }

                outputFile = new File(apkStorage, downloadFileName);
                if (!outputFile.exists()) {
                    outputFile.createNewFile();
                    Log.e(TAG, "File Created");
                }

                FileOutputStream fos = new FileOutputStream(outputFile);
                InputStream is = c.getInputStream();
                byte[] buffer = new byte[1024];
                int len1;
                long total = 0;

                while ((len1 = is.read(buffer)) != -1) {
                    // 每次循环前检查任务是否被取消,如果是就中断
                    if (isCancelled()) {
                        break;
                    }

                    total += len1;
                    publishProgress((int) (total * 100 / fileLength)); // 用publishProgress触发进度更新
                    fos.write(buffer, 0, len1);
                }

                // 关闭流,不管是否取消都要执行
                fos.close();
                is.close();
                c.disconnect(); // 断开连接

            } catch (Exception e) {
                e.printStackTrace();
                outputFile = null;
                Log.e(TAG, "Download Error Exception " + e.getMessage());
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            // 在主线程更新进度,这是AsyncTask的标准方式
            progressDialog.setProgress(values[0]);
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
            // 任务被取消时,删除已下载的文件
            if (outputFile != null && outputFile.exists()) {
                boolean deleted = outputFile.delete();
                Log.e(TAG, "Canceled, deleted partial file: " + deleted);
            }
            // 可以在这里做一些其他清理工作
        }
    }
}

关键改动说明

  • 新增onCancelled方法:当任务被取消时,这个方法会被调用,在这里删除临时文件。
  • doInBackground中检查isCancelled():每次读取数据前都判断,如果任务已取消就跳出循环,终止下载。
  • 进度更新移到onProgressUpdate:移除了runOnUiThread,改用AsyncTask自带的进度更新机制,更规范且避免线程问题。
  • 提升outputFile为成员变量:这样在onCancelled里能直接访问到要删除的文件。
  • 修复上下文引用:把bookejtemaeat.this换成context,避免内存泄漏,同时在SD卡不存在时 fallback 到内部存储,增强鲁棒性。

这样修改后,点击取消按钮就能立即终止下载进程,并删除已经下载的部分文件了。

内容的提问来源于stack exchange,提问作者ahmmed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:07:55