>

Android多线程断点续传下载,Android实现多线程断点

- 编辑:澳门新葡亰平台游戏 -

Android多线程断点续传下载,Android实现多线程断点

安卓手艺学习图谱(持续革新中,迎接关怀)

断点续传原理
一、从上次的地方接二连三下载。

实际上断点续传的原理非常粗大略,从字面上理解,所谓断点续传正是从截止的地点重新下载。</br>断点:线程截止的地点。续传:从为止的职分再次下载。用代码解析就是:断点 : 当前线程已经下载达成的多寡长度。续传 : 向服务器央求上次线程截至任务然后的数量。原理理解了,效能完结起来也轻易。每当线程结束时就把已下载的多长写入记录文件,当再度下载时,从记录文件读取已经下载了的长短。而这几个长度正是所急需的断点。

小编们平常在开采进程中会蒙受下载的效果与利益达成,当大家下载中断时,又不愿意下一次从头开头继续下载,大家就须要用到断点续传了。

二、从上次写入的文书一而再写入。

续传的落成也简要,能够经过设置网络诉求参数,须求服务器从钦命的职分上马读取数据。而要达成那八个作用只须求运用到httpU库罗德Lconnection里面包车型客车setRequestProperty方法便足以完毕.

断点续传是指当下载中断后,再度下载时能够从上次的下载速度继续下载。由此我们得以剖判得出完毕那一个成效,大家要求实时保存下载进程,那样在下一次连任下载的时候再把下载进程读收取来,继续下载。大家任重先生而道远须要缓慢解决俩个难点:一、从上次的岗位三番五次下载。二、从上次写入的文件一连写入。

第一点,通过Http的GET请求中的setRequestProperty("Range", "bytes=" + 开始位置+ "-" + "结束位置")方法
第二点,通过RandomAccessFile可以在本地文件中继续写入文件。因此,实现断点续传我们可以按照以下步骤。
1.首先获取要下载的文件长度,用来设置RandomAccessFile(本地文件)的长度。
2.实时保存文件下载进度,这个功能我们可以用数据库来实现。
3.中断后再次下载时,读取进度,再从上次的下载进度继续下载,并在本地的文件继续写入。
public void setRequestProperty(String field, String newValue)

第一点,通过Http的GET央浼中的Android多线程断点续传下载,Android实现多线程断点续传。**setRequestProperty("Range", "bytes="

二十多线程结合断点续传下载

平时来说所示,便是向服务器央求500-一千以内的500个byte:

  • 开端地点+ "-" + "甘休地方")方法**,能够设置下载的数量的初始地点和终止位置。那样大家就可以从上次的下载地方再三再四下载。

多线程无非是将待下载的公文分为若干个部分开展下载并贯彻断点续传。

conn.setRequestProperty("Range", "bytes=" + 500 + "-" + 1000);

第二点,通过RandomAccessFile能够在当和姑件中三回九转写入文件。

1.同样,我们首先要获取待下载的文件的长度,用来为每个线程分配下载长度。通过HttpURLConnection.getContentLength()获取待下载的文件的长度。如下:
filesize=connection.getContentLength();
2.通过前面获取的下载文件的长度,为每个线程计算下载长度,即为每个线程设置下载的起始位置跟结束位置。通过HttpUrlConnection.setRequestProperty("Range", "bytes=" + 开始位置+ "-" + "结束位置")方法。如下图所示:
计算方法如下:
int block = (filesize % threadCount == 0) ? filesize / threadCount : filesize / threadCount + 1;
所以每个线程对应的起始位置跟结束位置分别为:i block, (i + 1) block(i从0开始)
3.通过RandomAccessFile可以在文件指定位置写入数据。如下:
mRandomAccessFile.seek(startPos)
4.为每个线程的下载的进度都保存数据,这样每次每次暂停后重新下载都重新读取下载进度,并且可以从上次位置重新下载。并且再本地文件中继续读入数据。

如上只是续传的一某些供给,当大家赢获得下载数据时,还索要将数据写入文件,而普通发File对象并不提供从钦点位置写入数据的成效,那一年,就需求动用到RandomAccessFile来促成从钦点地方给文件写入数据的魔法。

为此,达成断点续传我们可以遵照以下步骤。

public void seek(long offset)

1.首先收获要下载的文本长度,用来安装RandomAccessFile的尺寸。

日常来讲所示,就是从文件的的第九二十一个byte后最初写入数据。

2.供给知道下载中断时,文件下载到何地了,大家供给实时保存文件下载进程,那个效应我们能够用数据库来兑现。

raFile.seek;

3.中断后再度下载时,读取进程,再从上次的下载速度继续下载,并在本地的公文三番四次写入

而开首写入数据时还须求用到RandomAccessFile里面包车型地铁另外三个格局

4.实时的履新下载进度条,作为给客商的下载反馈。

public void write(byte[] buffer, int byteOffset, int byteCount)

多线程无非是将待下载的公文分为若干个部分实行下载并贯彻断点续传。

该措施的行使和OutputStream的write的选用如出一辙...

1.同样,大家第一要收获待下载的公文的长短,用来为各种线程分配下载长度。通过HttpULacrosseLConnection.getContentLength()获取待下载的文本的长度。如下:filesize=connection.getContentLength();

上述正是断点续传的规律。

2.通过前边获取的下载文件的长短,为各样线程计算下载长度,即为各个线程设置下载的前奏地方跟结束地方。通过HttpUrlConnection.setRequestProperty("Range", "bytes=" + 开第2地点+ "-" + "甘休地点")方法。如下图所示:[图表上传失利...(image-f5634c-1521193284969)]

而二十八线程断点续传正是在单线程的断点续传上延伸的,而多线程断点续传是把全副文件分割成多少个部分,每一种部分由一条线程施行下载,而每一条下载线程都要兑现断点续传功能。为了贯彻文件分割功用,大家必要使用到httpU大切诺基Lconnection的另外五个办法:

计算情势如下:int block = (filesize % threadCount == 0) ? filesize / threadCount : filesize / threadCount + 1;所以每一个线程对应的开局地点跟甘休地方分别为:**i block, block**

public int getContentLength()

3.通过RandomAccessFile能够在文件钦命地点写入数据。如下:mRandomAccessFile.seek

当呼吁成功时,能够由此该方法取获得文件的总司长度。每一条线程下载大小 = fileLength / THREAD_NUM

4.为各个线程的下载的速度都保存数据,那样每便每一趟暂停后重新下载都重复读取下载进程,并且能够从上次地点再一次下载。而且再当麻芋果件中延续读入数据。

如下图所示,描述的便是十六线程的下载模型:

第一来看布局,布局很简短,布局用ProgressBar来作为下载速度的报告,每当下载速度爆发变化,都会在那举行立异。

图片 1二十二十四线程下载描述

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:andro xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.administrator.downloaddemo.MainActivity"> <Button android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:text="开始下载"/> <Button android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="20dp" android:text="停止下载"/> <ProgressBar android: style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginRight="10dp" android:layout_marginLeft="10dp" android:layout_marginTop="50dp" /></RelativeLayout>

在三十二线程断点续传下载中,有少数内需极其注意:由于文件是分成多个部分是被差别的线程的同不时间下载的,那就需求,每一条线程都分别需求有二个断点记录,和多少个线程完毕情状的笔录;

接下去是用来兑现分界面更新的Handler,大家会传播这几个hander来更新UI。当下载开头(DOWNLOAD_START)、下载进行(DOWNLOAD_KEEP )、下载甘休(DOWNLOAD_COMPLETE)以及退步时(DOWNLOAD_FAIL)分别开展对应的操作。

平时来讲图所示:

private Handler mHandler=new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { switch { case Constant.DOWNLOAD_START: mProgressBar.setMax; break; case Constant.DOWNLOAD_KEEP: mProgressBar.setProgress; break; case Constant.DOWNLOAD_COMPLETE: Toast.makeText(MainActivity.this,"下载完成",Toast.LENGTH_SHORT).show(); String url=  msg.obj; DBManager.getInstance(MainActivity.this).delete; break; case Constant.DOWNLOAD_FAIL: Toast.makeText(MainActivity.this,"下载失败",Toast.LENGTH_SHORT).show(); String urlstr=  msg.obj; FileDownloader.getInstance().pauseDownload; break; case Constant.DOWNLOAD_ClLEAN:// do something break; default: break; } return true; } });

图片 2二十八线程记录

具体的下载完结类是FileDownLoder,通过调用statDownload()方法并传播文件下载链接,文件大小,文件名,线程数量以及前边的handler如下便最早了下载:FileDownloader.getInstance().init(context,handler,downloadurl,filesize,filename,threadcount).startDownload();

独有全体线程的下载状态都远在实现景况时,技能表示文件已经下载实现。达成记录的主意二种八种,笔者这里运用的是JDK自带的Properties类来记录下载参数。

先看FileDownload的伊始化:

通过原理的问询,便得以火速的陈设性出断点续传工具类的主干结构图

#FileDownloader.java public synchronized FileDownloader init(Context context, Handler handler, String downloadurl, int filesize, String filename, int threadCount) { Log.d(TAG, "Run in init"); this.context = context; this.handler = handler; this.downloadurl = downloadurl; this.filesize = filesize; this.filename = filename; this.threadCount = threadCount; if (downloadStatemap == null) { downloadStatemap = new HashMap<>(); } initDatas(); return this; } private void initDatas() { RandomAccessFile accessFile = null; File file; int block = (filesize % threadCount == 0) ? filesize / threadCount : filesize / threadCount + 1; try { file = new File; if (!file.getParentFile().exists { file.getParentFile().mkdirs(); } if (!DBManager.getInstance.isHasInfos(downloadurl)) { Log.d(TAG, "run download info"); for (int i = 0; i < threadCount; i++) { DownloadInfo info = new DownloadInfo(i, i * block,  * block, 0, downloadurl); DBManager.getInstance.saveInfo; } } accessFile = new RandomAccessFile(file, "rw"); if (accessFile.length() == filesize) { return; } accessFile.setLength; } catch (Exception e) { e.printStackTrace(); } finally { try { if (accessFile != null) { accessFile.close(); } } catch (IOException e) { e.printStackTrace(); } } }

图片 3这里写图片描述

开首化了文件下载链接,文件大小,文件名,线程数量以及handler等音讯,还也可以有多个比较首要的是往数据库中写入下载音信,假如这是贰个新建职分,就为它的种种下载线程早先化他的下载起先地点,截至地点。接下来大家看startDownload()那么些办法:

 package com.arialyy.frame.http.inf; import java.net.HttpURLConnection; /** * 在这里面编写你的业务逻辑 */ public interface IDownloadListener { /** * 取消下载 */ public void onCancel(); /** * 下载失败 */ public void onFail(); /** * 下载预处理,可通过HttpURLConnection获取文件长度 */ public void onPreDownload(HttpURLConnection connection); /** * 下载监听 */ public void onProgress(long currentLocation); /** * 单一线程的结束位置 */ public void onChildComplete(long finishLocation); /** * 开始 */ public void onStart(long startLocation); /** * 子程恢复下载的位置 */ public void onChildResume(long resumeLocation); /** * 恢复位置 */ public void onResume(long resumeLocation); /** * 停止 */ public void onStop(long stopLocation); /** * 下载完成 */ public void onComplete(); }
#FileDownloader.java public synchronized void startDownload() { if (downloadStatemap.get(downloadurl) != null && downloadStatemap.get(downloadurl) == Constant.DOWNLOAD_STATE_START) { //已经正在下载中的话就不重新开启线程下载 Log.d(TAG, "download return"); return; } sendMessage(Constant.DOWNLOAD_START, filesize, -1, null); for (int i = 0; i < threadCount; i++) { ThreadPoolsUtil.getInstance().getCachedThreadPool().execute(new DownloadTask(context, handler, downloadurl, filesize, filename, i)); } }

该类是下载监听接口

老是开始下载都会向handler发送开首下载的message,主若是将文件大小传给ProgressBar。然后再依附线程数量开启下载职分。每一个DownloadTask都以多个Runnable。大家接下去看看DownloadTask。

import java.net.HttpURLConnection;/** * 下载监听 */public class DownloadListener implements IDownloadListener { @Override public void onResume(long resumeLocation) { } @Override public void onCancel() { } @Override public void onFail() { } @Override public void onPreDownload(HttpURLConnection connection) { } @Override public void onProgress(long currentLocation) { } @Override public void onChildComplete(long finishLocation) { } @Override public void onStart(long startLocation) { } @Override public void onChildResume(long resumeLocation) { } @Override public void onStop(long stopLocation) { } @Override public void onComplete() { }}

 /** * 子线程下载信息类 */ private class DownloadEntity { //文件总长度 long fileSize; //下载链接 String downloadUrl; //线程Id int threadId; //起始下载位置 long startLocation; //结束下载的文章 long endLocation; //下载文件 File tempFile; Context context; public DownloadEntity(Context context, long fileSize, String downloadUrl, File file, int threadId, long startLocation, long endLocation) { this.fileSize = fileSize; this.downloadUrl = downloadUrl; this.tempFile = file; this.threadId = threadId; this.startLocation = startLocation; this.endLocation = endLocation; this.context = context; } }
#DownloadTask.java public void run() { FileDownloader.getInstance().putDownloadState(mDownloadurl, Constant.DOWNLOAD_STATE_START); HttpURLConnection connection = null; BufferedInputStream inputStream = null; DownloadInfo info = new DownloadInfo(); Log.d(TAG, "is has info: " + DBManager.getInstance.isHasInfos(mDownloadurl)); if (DBManager.getInstance.isHasInfos(mDownloadurl)) { //判断是否存在未完成的该任务 info = DBManager.getInstance.getInfo(mDownloadurl, threadId); } try { URL url = new URL(mDownloadurl); int compeltesize = info.getCompeleteSize(); int startPos = info.getStartPos(); //本地数据库中的保存的开始位置跟结束位置 int endPos = info.getEndPos(); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod; connection.setConnectTimeout; connection.setReadTimeout; connection.setRequestProperty("Connection", "Keep-Alive"); connection.setRequestProperty("Range", "bytes=" + startPos+compeltesize + "-" + endPos); inputStream = new BufferedInputStream(connection.getInputStream; mRandomAccessFile = new RandomAccessFile(mFilename, "rw"); mRandomAccessFile.seek(startPos+compeltesize); //上次的最后的写入位置 byte[] buffer = new byte[8 * 1024]; int length = 0; while ((length = inputStream.read > 0) { if (FileDownloader.getInstance().getDownloadState(mDownloadurl) == Constant.DOWNLOAD_STATE_PAUSE) { //下载任务被暂停 return; }// Log.d(TAG, "write file length: " + length); mRandomAccessFile.write(buffer, 0, length); compeltesize += length;// Log.d(TAG,"save completesize is: "+compeltesize); DBManager.getInstance.updataInfos(threadId, compeltesize, mDownloadurl); //保存数据库中的下载进度 sendMessage(Constant.DOWNLOAD_KEEP, calculateCompeltesize(), -1, null); //更新进度条 } Log.d(TAG, "calculateCompeltesize: " + calculateCompeltesize() + " filesize: " + size + "threadid: " + threadId); if (calculateCompeltesize() >= size) { //判断下载是否完成 sendMessage(Constant.DOWNLOAD_COMPLETE, -1, -1, mDownloadurl); } } catch (Exception e) { sendMessage(Constant.DOWNLOAD_FAIL, -1, -1, mDownloadurl); e.printStackTrace(); } finally { try { if (inputStream != null) { inputStream.close(); } if (connection != null) { connection.disconnect(); } if (mRandomAccessFile != null) { mRandomAccessFile.close(); } sendMessage(Constant.DOWNLOAD_ClLEAN, -1, -1, mDownloadurl); } catch (IOException e) { e.printStackTrace(); } } }

此类是下载新闻配置类,每一条子线程的下载都急需贰个下载实体来安插下载音讯。

下载进度中会从数据库读取下载地点的新闻,依照那些音信来写入数据到地面文件。并透过handler来更新进程条。况且经过FileDownloader.getInstance().getDownloadState(mDownloadurl)实时检查实验下载状态是还是不是被中断了。暂停通过调用以下函数:

 /** * 多线程下载任务类 */ private class DownLoadTask implements Runnable { private static final String TAG = "DownLoadTask"; private DownloadEntity dEntity; private String configFPath; public DownLoadTask(DownloadEntity downloadInfo) { this.dEntity = downloadInfo; configFPath = dEntity.context.getFilesDir().getPath() + "/temp/" + dEntity.tempFile.getName() + ".properties"; } @Override public void run() { try { L.d(TAG, "线程_" + dEntity.threadId + "_正在下载【" + "开始位置 : " + dEntity.startLocation + ",结束位置:" + dEntity.endLocation + "】"); URL url = new URL(dEntity.downloadUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //在头里面请求下载开始位置和结束位置 conn.setRequestProperty("Range", "bytes=" + dEntity.startLocation + "-" + dEntity.endLocation); conn.setRequestMethod; conn.setRequestProperty("Charset", "UTF-8"); conn.setConnectTimeout; conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"); conn.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*"); conn.setReadTimeout; //设置读取流的等待时间,必须设置该参数 InputStream is = conn.getInputStream(); //创建可设置位置的文件 RandomAccessFile file = new RandomAccessFile(dEntity.tempFile, "rwd"); //设置每条线程写入文件的位置 file.seek(dEntity.startLocation); byte[] buffer = new byte[1024]; int len; //当前子线程的下载位置 long currentLocation = dEntity.startLocation; while ((len = is.read != -1) { if  { L.d(TAG, "++++++++++ thread_" + dEntity.threadId + "_cancel ++++++++++"); break; } if  { break; } //把下载数据数据写入文件 file.write(buffer, 0, len); synchronized (DownLoadUtil.this) { mCurrentLocation += len; mListener.onProgress(mCurrentLocation); } currentLocation += len; } file.close(); is.close(); if  { synchronized (DownLoadUtil.this) { mCancelNum++; if (mCancelNum == THREAD_NUM) { File configFile = new File(configFPath); if (configFile.exists { configFile.delete(); } if (dEntity.tempFile.exists { dEntity.tempFile.delete(); } L.d(TAG, "++++++++++++++++ onCancel +++++++++++++++++"); isDownloading = false; mListener.onCancel(); System.gc(); } } return; } //停止状态不需要删除记录文件 if  { synchronized (DownLoadUtil.this) { mStopNum++; String location = String.valueOf(currentLocation); L.i(TAG, "thread_" + dEntity.threadId + "_stop, stop location ==> " + currentLocation); writeConfig(dEntity.tempFile.getName() + "_record_" + dEntity.threadId, location); if (mStopNum == THREAD_NUM) { L.d(TAG, "++++++++++++++++ onStop +++++++++++++++++"); isDownloading = false; mListener.onStop(mCurrentLocation); System.gc(); } } return; } L.i(TAG, "线程【" + dEntity.threadId + "】下载完毕"); writeConfig(dEntity.tempFile.getName() + "_state_" + dEntity.threadId, 1 + ""); mListener.onChildComplete(dEntity.endLocation); mCompleteThreadNum++; if (mCompleteThreadNum == THREAD_NUM) { File configFile = new File(configFPath); if (configFile.exists { configFile.delete(); } mListener.onComplete(); isDownloading = false; System.gc(); } } catch (MalformedURLException e) { e.printStackTrace(); isDownloading = false; mListener.onFail(); } catch (IOException e) { FL.e(this, "下载失败【" + dEntity.downloadUrl + "】" + FL.getPrintException; isDownloading = false; mListener.onFail(); } catch (Exception e) { FL.e(this, "获取流失败" + FL.getPrintException; isDownloading = false; mListener.onFail(); } }
#FileDownloader.java public synchronized void pauseDownload(String downloadurl) { downloadStatemap.put(downloadurl, Constant.DOWNLOAD_STATE_PAUSE); } public void pauseAll() { if (downloadStatemap == null) { return; } for (String key:downloadStatemap.keySet{ downloadStatemap.put(key,Constant.DOWNLOAD_STATE_PAUSE); } }

以此是每条下载子线程的下载职责类,子线程通过下载实体对每一条线程进行下载配置,由于在多断点续传的定义里,结束表示的是搁浅状态,而回复表示的是线程从记录的断点重新张开下载,所以,线程处于终止状态时是不可能去除记录文件的。

在FileDownLoder里面有着保存着各种下载U景逸SUVL对应的下载状态的Hashmap。能够经过设置这一个url的下载状态来跟新职分的下载状态。

 /** * 多线程断点续传下载文件,暂停和继续 * * @param context 必须添加该参数,不能使用全局变量的context * @param downloadUrl 下载路径 * @param filePath 保存路径 * @param downloadListener 下载进度监听 {@link DownloadListener} */ public void download(final Context context, @NonNull final String downloadUrl, @NonNull final String filePath, @NonNull final DownloadListener downloadListener) { isDownloading = true; mCurrentLocation = 0; isStop = false; isCancel = false; mCancelNum = 0; mStopNum = 0; final File dFile = new File; //读取已完成的线程数 final File configFile = new File(context.getFilesDir().getPath() + "/temp/" + dFile.getName() + ".properties"); try { if (!configFile.exists { //记录文件被删除,则重新下载 newTask = true; FileUtil.createFile(configFile.getPath; } else { newTask = false; } } catch (Exception e) { e.printStackTrace(); mListener.onFail(); return; } newTask = !dFile.exists(); new Thread(new Runnable() { @Override public void run() { try { mListener = downloadListener; URL url = new URL(downloadUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod; conn.setRequestProperty("Charset", "UTF-8"); conn.setConnectTimeout; conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"); conn.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*"); conn.connect(); int len = conn.getContentLength(); if (len < 0) { //网络被劫持时会出现这个问题 mListener.onFail(); return; } int code = conn.getResponseCode(); if (code == 200) { int fileLength = conn.getContentLength(); //必须建一个文件 FileUtil.createFile; RandomAccessFile file = new RandomAccessFile(filePath, "rwd"); //设置文件长度 file.setLength(fileLength); mListener.onPreDownload; //分配每条线程的下载区间 Properties pro = null; pro = Util.loadConfig(configFile); int blockSize = fileLength / THREAD_NUM; SparseArray<Thread> tasks = new SparseArray<>(); for (int i = 0; i < THREAD_NUM; i++) { long startL = i * blockSize, endL =  * blockSize; Object state = pro.getProperty(dFile.getName() + "_state_" + i); if (state != null && Integer.parseInt(state + "") == 1) { //该线程已经完成 mCurrentLocation += endL - startL; L.d(TAG, "++++++++++ 线程_" + i + "_已经下载完成 ++++++++++"); mCompleteThreadNum++; if (mCompleteThreadNum == THREAD_NUM) { if (configFile.exists { configFile.delete(); } mListener.onComplete(); isDownloading = false; System.gc(); return; } continue; } //分配下载位置 Object record = pro.getProperty(dFile.getName() + "_record_" + i); if (!newTask && record != null && Long.parseLong(record + "") > 0) { //如果有记录,则恢复下载 Long r = Long.parseLong(record + ""); mCurrentLocation += r - startL; L.d(TAG, "++++++++++ 线程_" + i + "_恢复下载 ++++++++++"); mListener.onChildResume; startL = r; } if (i == (THREAD_NUM - 1)) { endL = fileLength;//如果整个文件的大小不为线程个数的整数倍,则最后一个线程的结束位置即为文件的总长度 } DownloadEntity entity = new DownloadEntity(context, fileLength, downloadUrl, dFile, i, startL, endL); DownLoadTask task = new DownLoadTask; tasks.put(i, new Thread; } if (mCurrentLocation > 0) { mListener.onResume(mCurrentLocation); } else { mListener.onStart(mCurrentLocation); } for (int i = 0, count = tasks.size(); i < count; i++) { Thread task = tasks.get; if (task != null) { task.start(); } } } else { FL.e(TAG, "下载失败,返回码:" + code); isDownloading = false; System.gc(); mListener.onFail(); } } catch (IOException e) { FL.e(this, "下载失败【downloadUrl:" + downloadUrl + "】n【filePath:" + filePath + "】" + FL.getPrintException; isDownloading = false; mListener.onFail.start(); }

末了,附上效果图:

实际上也没啥好说的,注释已经很完整了,须求留意两点1、恢复生机下载时:已下载的文件大小 = 该线程的上一次断点的位置 - 该线程起始下载位置;2、为了保证下载文件的完整性,只要记下文件空中楼阁就需求重新开展下载;

图片 4112141x9cwzm9vqwmm44hh.gif

图片 5末尾效果

类型地址:

Demo点我

本文由java编程发布,转载请注明来源:Android多线程断点续传下载,Android实现多线程断点