DownloadManager分析(1)
Summary
DownloadManager
是Android系统封装好的一个功能,位于/android/app/目录下,实现了下载功能。文档是这么说的:
download manager是一个处理长时间http下载的系统服务。客户端可以发出请求,让URI下载到特定的文件中。download manager会在后台执行下载,处理HTTP交互,并且在网络变化之后导致连接失败/系统重启后能重新尝试下载。通过传递
DOWNLOAD_SERVICE
给getSystemService
来获得DownloadManager实例。通过这个API请求Download的App应该注册一个ACTION_NOTIFICATION_CLICKED
来处理用户在Notification或者UI中点击一个正在下载的进度的操作。
SystemService的启动
读完了Summary这段话有一个疑问,为什么通过getSystemService
才能获取实例。在Context的源码中发现,getSystemService
是在Context
中实现的抽象方法,具体是在framework的ContextImpl.java
中实现的,我们看到有一个专门盛放SystemService的SYSTEM_SERVICE_MAP
:
@Override
public Object getSystemService(String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}
看来是有人往这个SYSTEM_SERVICE_MAP
里面加ServiceName了。在ContextImpl里面,有一个函数在往MAP
里填东西:
private static void registerService(String serviceName, ServiceFetcher fetcher) {
if (!(fetcher instanceof StaticServiceFetcher)) {
fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
}
//系统启动时往
SYSTEM_SERVICE_MAP.put(serviceName, fetcher);
}
然后我们看到,同样是在这个类里面实现了所有系统Service的注册:
//通过static方法优先注册所有的service。
static {
registerService(ACCESSIBILITY_SERVICE, new ServiceFetcher() {
public Object getService(ContextImpl ctx) {
return AccessibilityManager.getInstance(ctx);
}});
...
...
好了,上面我们看到了Service
被ServerFetcher
取出后添加进MAP
的动作;那么这些服务是谁初始化的?答案是SystemServer
。SystemServer
是Android的核心进程,由zygote
创建。所有服务都循环创建在SystemServer
上。
另外,ContextImpl跟Context是什么关系?我们在Application.java里面可以看到有ContextImpl的初始化,但是Context跟ContextImpl表面上并没有继承关系,虽然后者确实是前者的实现。
好了,我们得到一个结论,系统启动的时候,zygote帮忙启动了DownloadService这个SystemService,运行在SystemServer进程上。
从调用开始
回到最开始,我们最终开始下载,使用的是dowloadManager.enqueue(request);
这样的调用方式,enqueue
也就是把一个request加入队列,downloadmanager准备好了并且网络正常的时候会立刻开始下载。
public long enqueue(Request request) {
//把request添加进ContentValues(类似hashmap)里
ContentValues values = request.toContentValues(mPackageName);
Uri downloadUri = mResolver.insert(Downloads.Impl.CONTENT_URI, values);
long id = Long.parseLong(downloadUri.getLastPathSegment());
return id;
}
这里厉害了,toContentValues
方法的说明是:
ContentValues to be passed to DownloadProvider.insert()
也就是说这个request实际上是给DownloadProvider
准备的!唉,DownloadManager
看了半天,里面根本没有实现下载的操作,几乎全是「是否显示Notification」「下载进度查询」「下载成功监听」之类的管理性质的工作。
我查了资料里面说,在Android N之前的版本,这个DownloadProvider.insert()
执行之后,会直接以Context.startService的方式启动DownloadService
进行异步下载。先不说Android N之后的版本是怎么处理的,读到这里我脑中大概形成了这样一个模型:
-
DownloadManager
分发任务给DownloadProvider
-
DownloadProvider
调用DownloadService
-
DownloadService
真正执行下载操作
明天继续看。
References:
[1]http://www.cnblogs.com/adm1989/p/4631129.html
[2]https://segmentfault.com/q/1010000004457662
[3]http://www.jianshu.com/p/c9dc04af2f54#