在我的应用程序中,我正在实现一种通过DownloadManager下载数据文件的机制。当用户开始下载时,旁边会显示“取消”按钮,允许用户取消正在进行的下载。
这些数据文件会定期在服务器上更新,因此用户需要不时再次下载相同的文件。文件名在更新中保持稳定。
由于用户可能会在下载过程中随时点击“取消”,因此我希望保留旧版本,直到下载成功完成。为此,我重命名现有文件,然后才启动下载。如果用户取消下载(如果由于某种原因下载失败),我想将备份文件还原到其原始位置。
对于Cancel案例,我最初添加以下代码,以便在单击“取消”按钮时运行:
if (downloadManager.remove(reference) > 0) {
if (destFile.exists())
destFile.delete();
backupFile.renameTo(destFile);
}
刷新文件时,旧文件会在下载开始前重命名。但是,取消下载后,部分文件和备份都消失了。
由于我已经使用FileObserver
来监控下载进度,因此我将其扩展为同时监视文件删除并生成日志消息。在logcat中,我看到同一个文件有两个删除事件,表示部分下载的文件被删除,备份被重命名,然后重命名的备份也被删除。
很公平,我想,显然DownloadManager负责在后台删除,所以我需要注意这一点。所以我修改了上面的事件处理程序,只是将文件路径存储在列表中,而不是进行任何文件操作。然后我修改了我的FileObserver
以将所有已删除的文件与列表进行比较:如果匹配,则重命名备份文件。另外,我为每个操作添加了日志输出。
但是,事件序列仍然有效:现在部分下载的文件被下载管理器删除,触发我的FileObserver
,然后重命名备份文件。之后,备份文件将被删除。
在我看来,好像下载管理器过于热心:当下载被取消时,它会删除下载的文件,然后检查它是否真的消失,如果仍然在该路径中找到文件,则重试删除。
如何解决此问题并阻止下载管理器删除未下载的文件?
答案 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以获取两个文件名。如果它们不同,我会删除旧文件,然后重命名新文件。