5

Android入门第57天-使用OKHttp多线程制作像迅雷一样的断点续传功能

 1 year ago
source link: https://blog.csdn.net/lifetragedy/article/details/128591573
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

今天我们将继续使用OkHttp组件并制作一个基于多线程的可断点续传的下载器来结束Android OkHttp组件的所有知识内容。在这一课里我们会在上一次课程的基础上增加SQLite的使用以便于我们的App可以暂存下载时的实时进度,每次下载开始都会判断是覆盖式还是续传式下载。同时由于Android自带的进度条太丑了,我们对它稍稍进行了一些美化。可以说今天这篇教程也是一篇阶段性的功能整合实验。

下面开始进入课程。

a215c928823b9bfcafed0072bb0c3d5d.png
  1. 使用SQLite进行下载时的进度信息的暂存;

  1. 自定义ProgressBar的样式;

断点下载的原理

如果你认真的在看完了上篇教程后并且脱离我的Sample代码自己动手实现了一个多线程下载器的话那么今天这篇教程对于你来说会变得相当的简单。

因为所谓的断点下载就是把每一条线程当前在下载的信息存入一个SQLite的表内。而断点下载就是通过暂存的信息去改变RandomAcessFile在写入时的seek。

当然这里面还伴随着一些小技巧,我们需要我们的APP的“STOP”动作可以打断正在下载的进度,打断后如果再次点击了“DOWNLOAD”按钮,此时各子线程做的任务为“续传”,续传的进度是否完成了呢这也需要子线程和主线程间进行状态通信。

需要知道每个子线程运行是否已经结束了

这边并不是需要知道每个子线程的返回、中间态。我们只是需要知道每一个子线程是否运行完了。

在平时开发中我们经常会面临这样的一种情况。比如说我们外部需要长时间的等待?或者也有开发搞了一个全局的栈去计算、也有用future接口的。很多时候往往为了取一个状态,开发创造了一堆的“轮子”,导致了整个项目代码过于复杂以及不好调试。因此这些手法都不是很优雅。今天笔者给各位推荐一种更为优雅的写法,以便于在外部判断每一个子线程是否都运行完毕了。

使用状态反转来不断check子线程状态

8e194b3bb41d6bcba5d05142ae19b8ea.png

其实它的核心思路是:

  1. 在外部有一个无限while 循环,while(notFinish);

  1. 循环入口上手就把循环终止, notFinish=false;

  1. 接着依次检查每一个子线程内的一个状态值-finish,这个值在每个子线程内任务结束后会设为true。只要这个值在外部被检测到不为true,那么把外部循环的状态再改为notFinish=true,以使得外部循环不断运行直到所有子线程检测下来都确为finish,此时外部的while循环跳出;

6a02fc84bc99216ecb656bd8acb892ab.png

每个子线程下载的实时信息存储

f1d5500742c7e39692fe294e46db7a9d.png

我们设计了一个这样的表结构用来存储下载的实时信息。

  • 每次下载进程开始时,先根据下载URL去该表中查出所有的下载信息。比如说我们开启了3个线程,那么对于同一个URL:/test.zip可以根据download_path查出3条数据。把3条数据的download_length相加拼在一起,如果<当前远程文件size说明上次下载没有完成,那么继续下载。否则新建一个空文件并把这个空文件的长度设定为远程资源文件的长度;

  • 每个子线程在下载时不断根据download_path update这张表里的数据把当前的实时进度写进去;

  • 下载完后根据download_path清空这个表里的数据;

Http Get请求如何支持断点续传

Request.addHeader("Range", "bytes=" + startPos + "-" + endPos)

假设线程编号从1开始,开了3个子线程,共有1-3个线程,线程编号为1-3,此处的startPos和endPos的计算公式如下:

  • startPos=每个线程分页下载文件大小*线程编号+上一次下载进度,如果线程为1号线程那么startPos=上一次的下载进度;

  • endPos=每个线程分页下载文件大小*当前线程编号-1,-1代表“不计算文件末尾结束符”;

自定义Android里的ProgressBar的样式

res\values\colors.xml文件中加入一个ProgressBar的底色theme_progressbar

这是一个很浅很淡的蓝色。

res\drawable\下,新建一个progressbar_color.xml



newCodeMoreWhite.png

在activity_main.xml文件里定义progressbar时引用这个progressbar_color.xml文件。

以上内容都准备好了,我们就可以进入全代码了。

24cee7cbc550a64b4245c742631d6d69.png


newCodeMoreWhite.png

DbOpeerateHelper.java



newCodeMoreWhite.png

DBService.java



newCodeMoreWhite.png

DownloadProgressListener.java

DWManagerInfor.java



newCodeMoreWhite.png

DownloadService.java

这是一个主要的用于启动多线程下载和操作断点信息的类,在这个类内会分出3个子线程,每个子线程内又把这个类的this传入在子线程内进行回调、写下载时的实时信息入库、传递子线程状态,因此它是一个核心类。



newCodeMoreWhite.png

DownloadThread.java

这个类就是每一个子线程的实现了。在这个类里每一个子线程会启动OkHttp并使用http-header: Range去做断点下载。

值得注意的是,如果你的http-header带着Rnage去做请求,你得到的response code不是200还是206即:partial content。



newCodeMoreWhite.png

MainActivity.java



newCodeMoreWhite.png

为了正确运行上述内容你需要在gradle的build文件内加入OkHttp和commons-io的依赖包。

implementation 'com.squareup.okhttp3:okhttp:3.10.0'

implementation group: 'commons-io', name: 'commons-io', version: '2.6'

运行后的效果

当你无论如何stop再download再stop或者下载完后多次再download,那么当文件被成功下载后,会在Android的资源列表里此处显示下载的资源。

dc1c13dc488e977eed3341e2268f009c.png

它位于data\media\0下。

为了验证你下载的正确性,你可以把这个资源右键->另存出去。然后双击这个安装程序,如果它可以正确安装那么说明你的断点下载是正确了。

结束今天的课程,不妨自己动一下手试试看吧。

附、AndroidManifest.xml



newCodeMoreWhite.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK