DownloadManagerTest testMinimumDownload代码如下:
public void testMinimumDownload() throws Exception {
final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
try {
IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
mContext.registerReceiver(receiver, intentFilter);
long id = mDownloadManager.enqueue(new Request(getMinimumDownloadUrl()));
receiver.waitForDownloadComplete(LONG_TIMEOUT, id);
ParcelFileDescriptor fileDescriptor = mDownloadManager.openDownloadedFile(id);
assertEquals(MINIMUM_DOWNLOAD_BYTES, fileDescriptor.getStatSize());
Cursor cursor = null;
try {
cursor = mDownloadManager.query(new Query().setFilterById(id));
assertTrue(cursor.moveToNext());
assertEquals(DownloadManager.STATUS_SUCCESSFUL, cursor.getInt(
cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)));
assertEquals(MINIMUM_DOWNLOAD_BYTES, cursor.getInt(
cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)));
assertFalse(cursor.moveToNext());
} finally {
if (cursor != null) {
cursor.close();
}
}
assertRemoveDownload(id, 0);
} finally {
mContext.unregisterReceiver(receiver);
}
}
函数一开始调用DownloadManager的openDownloadFile:
public ParcelFileDescriptor openDownloadedFile(long id) throws FileNotFoundException {
return mResolver.openFileDescriptor(getDownloadUri(id), "r");
}
最终调用的是DownloadProvider的openFile:
@Override
public ParcelFileDescriptor openFile(final Uri uri, String mode) throws FileNotFoundException {
......
} else {
try {
// When finished writing, update size and timestamp
return ParcelFileDescriptor.open(file, pfdMode, mHandler, new OnCloseListener() {
@Override
public void onClose(IOException e) {
final ContentValues values = new ContentValues();
values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, file.length());
values.put(Downloads.Impl.COLUMN_LAST_MODIFICATION,
System.currentTimeMillis());
update(uri, values, null, null);
if (shouldScan) {
final Intent intent = new Intent(
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(file));
getContext().sendBroadcast(intent);
}
}
});
} catch (IOException e) {
throw new FileNotFoundException("Failed to open for writing: " + e);
}
}
......
}
这个方法返回ParcelFileDescriptor对象同时为其设置了一个OnCloseListener。
当下载完成之后会关闭这个ParcelFileDescriptor会调用这个OnCloseListener。然后后者会将ParcelFileDescriptor对应的文件大小信息更新到数据库中。
testMinimumDownload接着回去调用DownloadManager的enqueue,最终会导致DownloadProvider启动一个DownloadThread去下载对应的文件。
public class DownloadThread implements Runnable {
......
@Override
public void run() {
......
try {
......
executeDownload();
......
} catch(...) {
......
} finally {
logDebug("Finished with status " + Downloads.Impl.statusToString(mInfoDelta.mStatus));
mNotifier.notifyDownloadSpeed(mId, 0);
finalizeDestination();
mInfoDelta.writeToDatabase();
if (Downloads.Impl.isStatusCompleted(mInfoDelta.mStatus)) {
mInfo.sendIntentIfRequested();
}
TrafficStats.clearThreadStatsTag();
TrafficStats.clearThreadStatsUid();
netPolicy.unregisterListener(mPolicyListener);
if (wakeLock != null) {
wakeLock.release();
wakeLock = null;
}
}
.....
}
......
}
这里下载完成后会通知DownloadProvider下载完成,进而导致前面的ParcelFileDescriptor对象被close导致OnCloseListener执行。
同时DownloadThread继续执行finalizeDestination方法:
private void finalizeDestination() {
......
if (Downloads.Impl.isStatusError(mInfoDelta.mStatus)) {
......
} else if (Downloads.Impl.isStatusSuccess(mInfoDelta.mStatus)) {
// When success, open access if local file
if (mInfoDelta.mFileName != null) {
try {
// TODO: remove this once PackageInstaller works with content://
Os.chmod(mInfoDelta.mFileName, 0644);
} catch (ErrnoException ignored) {
}
if (mInfo.mDestination != Downloads.Impl.DESTINATION_FILE_URI) {
try {
// Move into final resting place, if needed
final File before = new File(mInfoDelta.mFileName);
final File beforeDir = Helpers.getRunningDestinationDirectory(
mContext, mInfo.mDestination);
final File afterDir = Helpers.getSuccessDestinationDirectory(
mContext, mInfo.mDestination);
if (!beforeDir.equals(afterDir)
&& before.getParentFile().equals(beforeDir)) {
final File after = new File(afterDir, before.getName());
if (before.renameTo(after)) {
mInfoDelta.mFileName = after.getAbsolutePath();
}
}
} catch (IOException ignored) {
}
}
}
}
......
}
这里会重命名下载的文件。如果这个操作比OnCloseListener先被执行就导致,onCloseListener将数据库的下载文件大小信息跟新为0.
从而导致testMinimumDownload后续查询下载文件大小做assert操作失败。