1.Downloadprovider 源码路径
Download的源码编译分为两个部分,一个是DownloadProvider.apk, 一个是DownloadProviderUi.apk.这两个apk的源码分别位于
packages/providers/DownloadProvider/ui/src
packages/providers/DownloadProvider/src
其中,DownloadProvider的部分是下载逻辑的实现,而DownloadProviderUi是界面部分的实现。
2.DownloadProvider下载的相关类
DownloadProvider -- 数据库操作的封装,继承自ContentProvider;
DownloadManager -- 大部分逻辑是进一步封装数据操作,供外部调用;
DownloadService -- 封装文件download,delete等操作,并且操纵下载的norification;继承自Service;
DownloadNotifier -- 状态栏Notification逻辑;
DownloadReceiver -- 配合DownloadNotifier进行文件的操作及其Notification;
DownloadList -- Download app主界面,文件界面交互;
3.系统下载流程
3.1 下载一般是从Browser里面点击链接开始
public static void startingDownload(Activity activity, String url, String userAgent, String contentDisposition,
String mimetype, String referer, boolean privateBrowsing, long contentLength, String filename, String downloadPath) {
WebAddress webAddress = new WebAddress(url);
webAddress.setPath(encodePath(webAddress.getPath()));
String addressString = webAddress.toString();
Uri uri = Uri.parse(addressString);
final DownloadManager.Request request = new DownloadManager.Request(uri);
request.setMimeType(mimetype);
..............
final DownloadManager manager = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);
new Thread("Browser download") {
public void run() {
manager.enqueue(request);//开启子线程下载
}
}.start();
}
在这个操作中,我们看到添加了request的各种参数,然后最后调用了DownloadManager的enqueue进行下载.
3.2 frameworks/base/core/java/android/app/DownloadManager.java
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函数中去.
/packages/providers/DownloadProvider/src/com/android/providers/downloads/DownloadProvider.java
@Override
public Uri insert(final Uri uri, final ContentValues values) {
......
//将相关的请求参数,配置等插入到downloads数据库;
long rowID = db.insert(DB_TABLE, null, filteredValues);
......
//将相关的请求参数,配置等插入到request_headers数据库中;
insertRequestHeaders(db, rowID, values);
......
//启动DownloadJobService下载及其它工作
try {
Helpers.scheduleJob(getContext(), rowID); //启动downloadprovider下载
} finally {
Binder.restoreCallingIdentity(token);
}
}
/packages/providers/DownloadProvider/src/com/android/providers/downloads/Helpers.java
public static boolean scheduleJob(Context context, DownloadInfo info) {
//关键代码
final JobScheduler scheduler = context.getSystemService(JobScheduler.class); // 使用Android JobScheduler/JobService 工作调度
final JobInfo.Builder builder = new JobInfo.Builder(jobId, new ComponentName(context, DownloadJobService.class));
scheduler.scheduleAsPackage(builder.build(), packageName, UserHandle.myUserId(), TAG);
}
/packages/providers/DownloadProvider/src/com/android/providers/downloads/DownloadJobService.java
@Override
public void onCreate() {
super.onCreate();
getContentResolver().registerContentObserver(ALL_DOWNLOADS_CONTENT_URI, true, mObserver); //注册download数据库监听
}
private ContentObserver mObserver = new ContentObserver(Helpers.getAsyncHandler()) {
@Override
public void onChange(boolean selfChange) {
Helpers.getDownloadNotifier(DownloadJobService.this).update(); //更新通知
}
};
@Override
public boolean onStartJob(JobParameters params) {
//关键代码
final int id = params.getJobId();
// Spin up thread to handle this download
final DownloadInfo info = DownloadInfo.queryDownloadInfo(this, id);
if (info == null) {
Log.w(TAG, "Odd, no details found for download " + id);
return false;
}
final DownloadThread thread;
synchronized (mActiveThreads) {
thread = new DownloadThread(this, params, info);
mActiveThreads.put(id, thread);
}
thread.start(); //开启子线程下载
return true;
}
/packages/providers/DownloadProvider/src/com/android/providers/downloads/DownloadThread.java
@Override
public void run() {
//关键代码
executeDownload(); //开始下载
}
private void executeDownload() throws StopRequestException {
//关键代码
URL url;
try {
// TODO: migrate URL sanity checking into client side of API
url = new URL(mInfoDelta.mUri);
} catch (MalformedURLException e) {
throw new StopRequestException(STATUS_BAD_REQUEST, e);
}
HttpURLConnection conn = null;
checkConnectivity();
conn = (HttpURLConnection) mNetwork.openConnection(url); //使用HttpsURLConnection下载
conn.setInstanceFollowRedirects(false);
conn.setConnectTimeout(DEFAULT_TIMEOUT);
conn.setReadTimeout(DEFAULT_TIMEOUT);
// If this is going over HTTPS configure the trust to be the same as the calling package.
if (conn instanceof HttpsURLConnection) {
((HttpsURLConnection)conn).setSSLSocketFactory(appContext.getSocketFactory());
}
addRequestHeaders(conn, resuming);
final int responseCode = conn.getResponseCode();
switch (responseCode) {
case HTTP_OK: //网络请求成功
if (resuming) {
throw new StopRequestException(STATUS_CANNOT_RESUME, "Expected partial, but received OK");
}
parseOkHeaders(conn);
transferData(conn); //Transfer data from the given connection to the destination file
return;
.............
}
文件下载的数据库路径:
com.android.providers.downloads/databases/downloads.db