【问题标题】:Android DownloadManager, backup files and canceled downloadsAndroid DownloadManager、备份文件和取消下载
【发布时间】:2016-06-02 20:19:56
【问题描述】:

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

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

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

对于取消的情况,我最初添加了以下代码,以便在单击取消按钮时运行:

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

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

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

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

然而,事件的顺序实际上仍然是相同的:现在下载管理器删除了部分下载的文件,触发了我的FileObserver,这将反过来重命名备份文件。之后,备份文件被删除。

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

我怎样才能解决这个问题并防止下载管理器删除它没有下载的文件?

【问题讨论】:

    标签: android android-download-manager


    【解决方案1】:

    我最终解决了多次删除问题,利用 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 实例描述,其中包含两个文件名的引用。我把它们放在三个Maps:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-06
      • 1970-01-01
      • 1970-01-01
      • 2015-07-05
      相关资源
      最近更新 更多