Android DownloadManager,备份文件和已取消的下载

时间:2016-06-02 20:19:57

标签: android android-download-manager

在我的应用程序中,我正在实现一种通过DownloadManager下载数据文件的机制。当用户开始下载时,旁边会显示“取消”按钮,允许用户取消正在进行的下载。

这些数据文件会定期在服务器上更新,因此用户需要不时再次下载相同的文件。文件名在更新中保持稳定。

由于用户可能会在下载过程中随时点击“取消”,因此我希望保留旧版本,直到下载成功完成。为此,我重命名现有文件,然后才启动下载。如果用户取消下载(如果由于某种原因下载失败),我想将备份文件还原到其原始位置。

对于Cancel案例,我最初添加以下代码,以便在单击“取消”按钮时运行:

if (downloadManager.remove(reference) > 0) {
    if (destFile.exists())
        destFile.delete();
    backupFile.renameTo(destFile);
}

刷新文件时,旧文件会在下载开始前重命名。但是,取消下载后,部分文件和备份都消失了。

由于我已经使用FileObserver来监控下载进度,因此我将其扩展为同时监视文件删除并生成日志消息。在logcat中,我看到同一个文件有两个删除事件,表示部分下载的文件被删除,备份被重命名,然后重命名的备份也被删除。

很公平,我想,显然DownloadManager负责在后台删除,所以我需要注意这一点。所以我修改了上面的事件处理程序,只是将文件路径存储在列表中,而不是进行任何文件操作。然后我修改了我的FileObserver以将所有已删除的文件与列表进行比较:如果匹配,则重命名备份文件。另外,我为每个操作添加了日志输出。

但是,事件序列仍然有效:现在部分下载的文件被下载管理器删除,触发我的FileObserver,然后重命名备份文件。之后,备份文件将被删除。

在我看来,好像下载管理器过于热心:当下载被取消时,它会删除下载的文件,然后检查它是否真的消失,如果仍然在该路径中找到文件,则重试删除。

如何解决此问题并阻止下载管理器删除未下载的文件?

1 个答案:

答案 0 :(得分:0)

我最终解决了多重删除问题,因为Android下载管理器永远不会覆盖现有文件,将自己的下载目标重命名为仍然可用的名称。

当再次下载文件时,我不打算将旧文件移开。下载管理器将检测到文件已存在,并为下载选择其他名称。下载成功完成后,我删除旧文件并重命名新文件。

唯一的挑战是确定下载管理器选择的文件名,因为Android似乎没有任何明确的通知。下载开始时没有触发意图,因此我不得不再次使用FileObserver

注意FileObserver.CREATE似乎是最直接的方式。但是,当我在此时查询下载列表时,查询将返回本地路径的空值。

因此我使用了FileObserver.MODIFY,它会在对文件的每次修改时触发。我已经用它来显示下载进度,此时必须有一个本地文件。第一次此事件触发下载管理器重命名的文件时,我将获得一个尚未列入我的列表的文件名。然后我运行以下代码:

        // File file: the file being downloaded
        // DownloadInfo info: information about a download in progress
        /* First progress report for a renamed file */
        DownloadManager.Query query = new DownloadManager.Query();
        query.setFilterByStatus(~(DownloadManager.STATUS_FAILED | DownloadManager.STATUS_SUCCESSFUL));
        Cursor cursor = downloadManager.query(query);
        if (!cursor.moveToFirst()) {
            cursor.close();
            return;
        }
        do {
            Long reference = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID));
            String path = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
            if (file.equals(new File(path))) {
                info = downloadsByReference.get(reference);
                if (info != null) {
                    info.downloadFile = file;
                    downloadsByFile.put(info.downloadFile, info);
                }
            }
        } while (cursor.moveToNext());
        cursor.close();

正在进行的每个下载都由DownloadInfo实例描述,其中包含两个文件名的引用。我将它们保存在三个Map中:

  • downloadsByReference使用下载管理器的ID作为密钥
  • downloadsByFile使用本地文件作为密钥
  • downloadsByUri使用URI作为密钥

下载完成后,我会在downloadsByReference中查找其ID以获取两个文件名。如果它们不同,我会删除旧文件,然后重命名新文件。