okdownloader源码分析
作者是流利说的Android架构负责人,本来有一个5k+ star的FileDownloader项目,但是又重新写了这个下载框架okdownloader,具体原因如下:
- FileDownloader framework is not easy to write unit-test, it is not a testable framework, so it is not stable enough.
- The core library of FileDownloader is too complex and not pure enough, so 5K+ star 1K+ fork with around 10 PR
相对于FileDownloader的优势
- 单元测试覆盖度非常高
- 更简单的接口
- 支持任务优先级
- 使用Uri标识文件来存储output-stream
- 核心代码更加轻量及纯净
- 更灵活的回调、监听机制
- 更容易扩展
- 保持性能的基础上减少线程数量
- 文件IO线程池独立于网络IO线程池
- response header中不包含文件名时,自动从URL中获取(使用正则表达式实现)
主框架分析
-
下载请求通过DownloadTask.Builder封装,通过各种set方法配置请求参数
-
全部下载策略定义在OkDownload中,如果需要自定义则需要通过内置的Builder构造出一个实例,再通过setSingletonInstance方法设置,替换默认实现
-
下载请求过程分为两个调用连(connect、fetch),在DownloadChain中分别通过#processConnect以及#processFetch的递归调用来实现
-
下载文件的分块逻辑实现在BreakpointInterceptor中,当DownloadCall创建新task时,首先将一个空的BlockInfo添加到BreakpointInfo#blockInfoList中并启动对应的DownloadChain,同时park当前线程直到第一个DownloadChain中的BreakpointInterceptor计算好分块数量,之后清空BreakpointInfo#blockInfoList并unpark当前线程,执行后续操作
下载流程.png -
多种下载回调,根据需求来选择DownloadListener1,DownloadListener2,etc..
-
多任务下载,使用DownloadContext来构造
-
cancel操作不会删除已下载的文件,只相当与暂停,重新下载时会从断点继续下载
//DownloadCall#execute 任务开始
void taskStart(DownloadTask task);
//DownloadCall#execute 从头开始
void downloadFromBeginning(DownloadTask task, BreakpointInfo info, ResumeFailedCause cause);
//DownloadCall#execute 从断点开始
void downloadFromBreakpoint(DownloadTask task, BreakpointInfo info);
//HeaderInterceptor#interceptConnect 创建链接开始
void connectStart(DownloadTask task, int blockIndex,
@NonNull Map<String, List<String>> requestHeaderFields);
//HeaderInterceptor#interceptConnect 创建链接结束
void connectEnd(DownloadTask task, int blockIndex, int responseCode,
@NonNull Map<String, List<String>> responseHeaderFields);
//BreakpointInterceptor#interceptConnect 分块结束
void splitBlockEnd(DownloadTask task, BreakpointInfo info);
//DownloadChain#start 开始下载
void fetchStart(DownloadTask task, int blockIndex, long contentLength);
//FetchDataInterceptor#interceptFetch->DownloadChain#flushNoCallbackIncreaseBytes 下载进度
void fetchProgress(DownloadTask task, int blockIndex, long increaseBytes);
//DownloadChain#start 下载结束
void fetchEnd(DownloadTask task, int blockIndex, long contentLength);
//DownloadCall#execute 任务结束
void taskEnd(DownloadTask task, EndCause cause, @Nullable Exception realCause);
代码结构
.
├── DownloadContext.java //多个下载任务串/并行下载,使用QueueSet来做设置
├── DownloadListener.java //下载状态回调接口定义
├── DownloadMonitor.java
├── DownloadSerialQueue.java
├── DownloadTask.java //单个下载任务
├── OkDownload.java //入口类,负责下载任务装配
├── OkDownloadProvider.java //单纯为了获取上下文Context
├── SpeedCalculator.java //下载速度计算
├── StatusUtil.java //获取DownloadTask下载状态,检查下载文件是否已经下载完成等
├── UnifiedListenerManager.java //多个listener管理
└── core
├── NamedRunnable.java //可命名的线程实现
├── Util.java //工具类
├── breakpoint
│ ├── BlockInfo.java //下载分块信息,记录当前块的下载进度,第0个记录整个下载任务的进度
│ ├── BreakpointInfo.java // BlockInfo聚合类,包含文件名、URL等信息
│ ├── BreakpointStore.java //下载过程中断点信息存储接口定义
│ └── BreakpointStoreOnCache.java //断点信息存储在缓存中的实现
├── cause
│ ├── EndCause.java //结束状态
│ └── ResumeFailedCause.java //下载异常原因
├── connection
│ ├── DownloadConnection.java // 下载链接接口定义
│ └── DownloadUrlConnection.java //下载链接UrlConnection实现
├── dispatcher
│ ├── CallbackDispatcher.java //DownloadListener分发代理(是否回调到UI线程,默认为true)
│ └── DownloadDispatcher.java //下载任务线程分配
├── download
│ ├── DownloadCache.java //MultiPointOutputStream包裹类
│ ├── DownloadCall.java //下载任务线程,包含DownloadTask、DownloadChain的list以及DownloadCache
│ ├── DownloadChain.java //持有DownloadTask、BreakpointInfo、DownloadCache、DownloadConnection等对象,链式调用各connect及fetch的Interceptor,开启下载任务
│ └── DownloadStrategy.java //下载策略,包括分包策略、下载文件命名策略以及response是否可用
├── exception //各种异常
│ ├── FileBusyAfterRunException.java
│ ├── InterruptException.java
│ ├── PreAllocateException.java
│ ├── ResumeFailedException.java
│ ├── RetryException.java
│ └── ServerCancelledException.java
├── file
│ ├── DownloadOutputStream.java //输出流接口定义
│ ├── DownloadUriOutputStream.java //Uri输出流实现
│ ├── MultiPointOutputStream.java //多block输出流管理
│ └── ProcessFileStrategy.java //下载过程中文件处理逻辑
├── interceptor
│ ├── BreakpointInterceptor.java //connect时分块,fetch时循环调用FetchDataInterceptor获取数据
│ ├── FetchDataInterceptor.java //fetch时读写流数据,记录增加bytes长度
│ ├── Interceptor.java
│ ├── RetryInterceptor.java //错误处理、connect时重试机制,fetch结束时同步输出流,确保写入数据完整
│ └── connect
│ ├── CallServerInterceptor.java //启动DownloadConnection
│ ├── HeaderInterceptor.java //添加头信息,调用connectStart、connectEnd
│ └── RedirectInterceptor.java //重定向相关处理
└── listener //多种回调及辅助接口
├── DownloadListener1.java
├── DownloadListener2.java
├── DownloadListener3.java
├── DownloadListener4.java
├── DownloadListener4WithSpeed.java
└── assist
├── Listener1Assist.java
├── Listener4Assist.java
└── Listener4SpeedAssistExtend.java