因为解一个bug,所以大体的看了下Android4.3 的DownloadProvider模块,现在通过在写博客的过程中进一步的熟悉它,首先其源码位置于package/provider/DownloadProvider;
界面部分代码在packages/providers/DownloadProvider/ui/src下;
逻辑部分代码位于packages/providers/DownloadProvider/src下;
首先介绍几个关键的类,来有个大体的认识:
1、DownloadProvider:看名字就知道它是数据库操作的封装,继承自ContentProvider;
2、DownloadManager:大部分逻辑是进一步封装数据操作,供外部调用;
3、DownloadService:封装文件download,delete等操作,并且操纵下载的norification;继承自Service;
4、DownloadNotifier:状态栏Notification逻辑;
5、DownloadReceiver:配合DownloadNotifier进行文件的操作及其Notification;
6、DownloadList:Download app主界面,文件界面交互;
接下来还需要看一下Download的DataBase结构;它包括两张表:downloads和request_headers,来看看创建代码:
private void createDownloadsTable(SQLiteDatabase db) {
try {
db.execSQL(“DROP TABLE IF EXISTS ” + DB_TABLE);
db.execSQL(“CREATE TABLE ” + DB_TABLE + “(” +
Downloads.Impl._ID + ” INTEGER PRIMARY KEY AUTOINCREMENT,” +
Downloads.Impl.COLUMN_URI + ” TEXT, ” +
Constants.RETRY_AFTER_X_REDIRECT_COUNT + ” INTEGER, ” +
Downloads.Impl.COLUMN_APP_DATA + ” TEXT, ” +
Downloads.Impl.COLUMN_NO_INTEGRITY + ” BOOLEAN, ” +
Downloads.Impl.COLUMN_FILE_NAME_HINT + ” TEXT, ” +
Constants.OTA_UPDATE + ” BOOLEAN, ” +
Downloads.Impl._DATA + ” TEXT, ” +
Downloads.Impl.COLUMN_MIME_TYPE + ” TEXT, ” +
Downloads.Impl.COLUMN_DESTINATION + ” INTEGER, ” +
Constants.NO_SYSTEM_FILES + ” BOOLEAN, ” +
Downloads.Impl.COLUMN_VISIBILITY + ” INTEGER, ” +
Downloads.Impl.COLUMN_CONTROL + ” INTEGER, ” +
Downloads.Impl.COLUMN_STATUS + ” INTEGER, ” +
Downloads.Impl.COLUMN_FAILED_CONNECTIONS + ” INTEGER, ” +
Downloads.Impl.COLUMN_LAST_MODIFICATION + ” BIGINT, ” +
Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE + ” TEXT, ” +
Downloads.Impl.COLUMN_NOTIFICATION_CLASS + ” TEXT, ” +
Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS + ” TEXT, ” +
Downloads.Impl.COLUMN_COOKIE_DATA + ” TEXT, ” +
Downloads.Impl.COLUMN_USER_AGENT + ” TEXT, ” +
Downloads.Impl.COLUMN_REFERER + ” TEXT, ” +
Downloads.Impl.COLUMN_TOTAL_BYTES + ” INTEGER, ” +
Downloads.Impl.COLUMN_CURRENT_BYTES + ” INTEGER, ” +
Constants.ETAG + ” TEXT, ” +
Constants.UID + ” INTEGER, ” +
Downloads.Impl.COLUMN_OTHER_UID + ” INTEGER, ” +
Downloads.Impl.COLUMN_TITLE + ” TEXT, ” +
Downloads.Impl.COLUMN_DESCRIPTION + ” TEXT, ” +
Constants.MEDIA_SCANNED + ” BOOLEAN);”);
} catch (SQLException ex) {
Log.e(Constants.TAG, “couldn’t create table in downloads database”);
throw ex;
}
}
private void createHeadersTable(SQLiteDatabase db) {
db.execSQL(“DROP TABLE IF EXISTS ” + Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE);
db.execSQL(“CREATE TABLE ” + Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE + “(” +
“id INTEGER PRIMARY KEY AUTOINCREMENT,” +
Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + ” INTEGER NOT NULL,” +
Downloads.Impl.RequestHeaders.COLUMN_HEADER + ” TEXT NOT NULL,” +
Downloads.Impl.RequestHeaders.COLUMN_VALUE + ” TEXT NOT NULL” +
“);”);
}
解析需要按照一个主要的流程来讲解,我就以Browser中下载文件来作为讲解的入口吧;
首先,当我在网页中点击下载文件按钮后,开始下载文件,这里Browser中调用的代码是:
…..
//初始化一个Uri对象
Uri uri = Uri.parse(addressString);
final DownloadManager.Request request;
try {
request = new DownloadManager.Request(uri);
} catch (IllegalArgumentException e) {
Toast.makeText(activity, R.string.cannot_download, Toast.LENGTH_SHORT).show();
return;
}
request.setMimeType(mimetype);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
request.allowScanningByMediaScanner();
request.setDescription(webAddress.getHost());
String cookies = CookieManager.getInstance().getCookie(url, privateBrowsing);
request.addRequestHeader(“cookie”, cookies);
request.addRequestHeader(“User-Agent”, userAgent);
request.addRequestHeader(“Referer”, referer);
request.setNotificationVisibility(
DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
…..
final DownloadManager manager
= (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);
new Thread(“Browser download”) {
public void run() {
manager.enqueue(request);
}
}.start();
…..
获得DownloadManager的实例,组建一个DownloadManager.Request参数,在线程中调用其enqueue函数进行下载请求;看看这个qnqueue函数做了什么:
public long enqueue(Request request) {
ContentValues values = request.toContentValues(mPackageName);
Uri downloadUri = mResolver.insert(Downloads.Impl.CONTENT_URI, values);
long id = Long.parseLong(downloadUri.getLastPathSegment());
return id;
}
enqueue函数主要是将Rquest实例分解组成一个ContentValues实例,并且添加到数据库中,函数返回插入的这条数据返回的ID;
ContentResolver.insert函数会调用到DownloadProvider实现的ContentProvider的insert函数中去;
……
//将相关的请求参数,配置等插入到downloads数据库;
long rowID = db.insert(DB_TABLE, null, filteredValues);
……
//将相关的请求参数,配置等插入到request_headers数据库中;
insertRequestHeaders(db, rowID, values);
……
if (values.getAsInteger(Downloads.Impl.COLUMN_DESTINATION) ==
Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD) {
// When notification is requested, kick off service to process all
// relevant downloads.
//启动DownloadService进行下载及其它工作
if (Downloads.Impl.isNotificationToBeDisplayed(vis)) {
context.startService(new Intent(context, DownloadService.class));
}
} else {
context.startService(new Intent(context, DownloadService.class));
}
notifyContentChanged(uri, match);
return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, rowID);
接下来就是DownloadService,应该是大头了,主要的下载任务就是通过它来完成的;startService启动DownloadService,首先当然是OnCreate函数了:
@Override
public void onCreate() {
super.onCreate();
……
//mUpdateThread 以及mUpdateHandler
mUpdateThread = new HandlerThread(TAG + “-UpdateThread”);
mUpdateThread.start();
mUpdateHandler = new Handler(mUpdateThread.getLooper(), mUpdateCallback);
mScanner = new DownloadScanner(this);
//DownloadNotifier对象
mNotifier = new DownloadNotifier(this);
mNotifier.cancelAll();
mObserver = new DownloadManagerContentObserver();
//注册监听Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI的Observer
getContentResolver().registerContentObserver(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
true, mObserver);
}
创建的一个HandlerThread对象mUpdateThread并且start;用Handler、Message来进行后续的逻辑处理;
再来看看OnStartCommand函数:
private Handler.Callback mUpdateCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
final int startId = msg.arg1;
final boolean isActive;
synchronized (mDownloads) {
isActive = updateLocked();
}
……
if (isActive) {
//如果Active,则会在Delayed 5×60000ms后发送MSG_FINAL_UPDATE Message,主要是为了“any finished operations that didn’t trigger an update pass.”
enqueueFinalUpdate();
} else {
//如果没有Active的任务正在进行,就会停止Service以及其它
if (stopSelfResult(startId)) {
if (DEBUG_LIFECYCLE) Log.v(TAG, “Nothing left; stopped”);
getContentResolver().unregisterContentObserver(mObserver);
mScanner.shutdown();
mUpdateThread.quit();
}
}
return true;
}
};
重点是updateLocked()函数,我们来看看;
private boolean updateLocked() {
final long now = mSystemFacade.currentTimeMillis();
boolean isActive = false;
long nextActionMillis = Long.MAX_VALUE;
//mDownloads初始化是一个空的Map