Android 多线程断点续传同时下载多个大文件

最近学习在Android环境中一些网络请求方面的知识,其中有一部分是关于网络下载方面的知识。在这里解析一下自己写的demo,总结一下自己所学的知识。下图为demo的效果图,仿照一些应用下载商城在ListView中列出加载项,然后可以可以下载和停止。

1.概述

这里有几个比较重要的类DownloadManager、DownloadService、DownloadTask、ThreadDAOImpl。主要的下载流程如下。
(1) DownloadManager 负责下载任务的调配,以及下载服务DownloadService的启动
(2) DownloadService 主获取下载文件的的一些信息,包括文件的名字、文件的长度等,并创建下载任务DownloadTask
(3) DownloadTask 是正式下载文件的类,首先查看数据库里有没保存过相应的断点,并从相应的断点开始下载,如果没有则将文件分段,并启动下载
(4) ThreadDAOImpl 数据库操作类,主要是保存线程下载的断点信息

2.多线程断点续传

当然这里最核心的部分就是多线程断点续传,原来不是很难,就是将要下载的文件分割成多个部分,每个部分使用的不同的线程同时下载。

2.1获取下载文件长度,设置本地文件

在DownloadService 设置下载文件的信息,如下一段代码:

 class InitThread extends Thread {
//        FileInfo fileInfo;
        TaskInfo taskInfo;
        public InitThread (TaskInfo taskInfo) {
            this.taskInfo = taskInfo;
        }
        @Override
        public void run() {
            super.run();
            Log.i(tag,"InitThread");
            try {
                URL url = new URL(taskInfo.getUrl());
                HttpURLConnection con = (HttpURLConnection) url.openConnection();
                con.setRequestMethod("GET");
                con.setConnectTimeout(5000);
                if(con.getResponseCode() ==  HttpURLConnection.HTTP_OK) {
                    int len = con.getContentLength(); <span style="color:#ff0000;">//文件的总长度</span>
                    taskInfo.setLenght(len);
                    if(len <= 0) {
                        return;
                    }
                  …………此处省略部分
                    //start 设置下载文件
                   <span style="color:#ff6666;"> RandomAccessFile accessFile = new RandomAccessFile(new File(taskInfo.getFilePath(),taskInfo.getFileName()),"rwd");
                    accessFile.setLength(len); //设置文件长度</span>
                    accessFile.close();
                    //end 设置下载文件
                …………此处省略部分

                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

2.2 文件分段

接下的工作是分段下载
举个列子一个10M的文件,分成三份求整10/3 = 3,前面的一二份分别是3M,最后一份是4M。所以第一份从0~2.9,第二份是从3~5.9,第三份是从6~10,这里只是粗来的说明。接下来看代码,在DownloadTask中有如下有如下代码:
/**
 * 启动下载
 */
public void downlaod() {

     …………此处省略部分
    //start 数据库没有对应的线程信息,则创建相应的线程信息
    if(threadInfoList.size() <=0) {
        <span style="color:#ff6666;">int block = mTaskInfo.getLenght()/mThreadCount; //将下载文件分段,每段的长度</span>
        if(block > 0) {
            //start 根据线程数量分别建立线程信息
            for(int i = 0;i < mThreadCount;i++) {
                ThreadInfo info = new ThreadInfo(i,mTaskInfo.getUrl(),i*block,(i+1)*block-1,0);
                if(i == mThreadCount -1) {
                   <span style="color:#ff0000;"> info.setEnd(mTaskInfo.getLenght()); //分段最后一个,结束位置到文件总长度末尾</span>
                }
                threadInfoList.add(info);         //加入列表
                mThreadDao.insertThread(info);   //向数据库插入线程信息
            }
            //end 根据线程数量分别建立线程信息
        }else {
            ThreadInfo info = new ThreadInfo(0,mTaskInfo.getUrl(),0,mTaskInfo.getLenght(),0);
            threadInfoList.add(info);
            mThreadDao.insertThread(info);
        }
    }
    //end 数据库中没有对应的线程信息,则创建相应的线程信息

    …………此处省略部分
}

2.3下载线程

下面是主要的下载文件的线程

主要的是设置开始读取和结束的地方:

con.setRequestProperty("Range", "bytes=" + start + "-" + threadInfo.getEnd()); //设置读取文件的位置,和结束位置

写入本地文件的地方:

accessFile.seek(start);    //设置开始写入的位置
/**
     * 下载线程
     */
    class DownloadThread extends  Thread {
        …………此处省略部分
        @Override
        public void run() {
                …………此处省略部分
                    int start = threadInfo.getStart()+threadInfo.getFinished(); //读取文件的位置
                    //start 初始化下载链接
                    …………此处省略部分                    
con.setRequestProperty("Range", "bytes=" + start + "-" + threadInfo.getEnd()); //设置读取文件的位置,和结束位置
                    //end 初始化下载链接
                    //start 初始化下载到本地的文件
                    <span style="color:#ff0000;">accessFile  = new RandomAccessFile(new File(mTaskInfo.getFilePath(), mTaskInfo.getFileName()),"rwd");
                    accessFile.seek(start);    //设置开始写入的位置</span>
                    //end 初始化下载到本地的文件

                   …………此处省略部分
  while((readLen = inputStream.read(buffer))!=-1) {
                          <span style="background-color: rgb(255, 255, 255);"><span style="color:#ff0000;">  accessFile.write(buffer, 0, readLen);</span></span>
//                            Log.i(tag, "readLen = " + readLen);
                            finished += readLen;
                            threadInfo.setFinished(finished);    //设置已经下载进度
                            if(System.currentTimeMillis() - time >2000) {
//                                Log.i(tag, "readLen = " + readLen);
                                notifyProgress(threadInfo.getId(), finished); //每隔2秒通知下载进度
                                time = System.currentTimeMillis();
                            }
                            //start 停止下载,保存进度
                            if(isPause) {
                                Log.i(tag,"pause name = "+mTaskInfo.getFileName());
                                notifyProgress(threadInfo.getId(), finished);        //通知下载进度
                                mThreadDao.updateThread(threadInfo.getUrl(),threadInfo.getId(),finished);  //更新数据库对应的线程信息
                                return;
                            }
                            //end 停止下载,保存进度
                        }
                        //end 读取输入流写入文件

                 …………此处省略部分                    }
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (ProtocolException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {

                         …………此处省略部分
                }
            super.run();
        }
    }
 

2.4保存断点

在上面的DownloadThread下载线程中保存断点信息,是使用数据库形式保存的。

mThreadDao.updateThread(threadInfo.getUrl(),threadInfo.getId(),finished);  //更新数据库对应的线程信息

3其他辅助类

(1) 数据库操作类ThreadDAOImpl,断点信息的增、改、查、删。
(2) 回调接口OnDownload,下载进度以及下载完成
(3) 下载任务信息TaskInfo
(4) 线程信息ThreadInfo

4线程池

由于使用到多线程同时下载,这里在使用了线程池管理。在DownloadService类中创建并初始化线程池,按照CPU核心数量乘以2再加1设置线程池中线程的数量
mThreadPool = Executors.newFixedThreadPool(getNumberOfCPUCores()*2+1);  //初始化线程
在下载时使用三个线程同时下载
DownloadTask task = new DownloadTask(DownloadService.this,info,mThreadPool,3); //建立下载任务,3个线程同时下载

由于线程池内的线程数量是有限的,当启动下载之后有空闲的线程会马上执行,如果没有就只能等待下载任务完成再下载。

时间: 2024-10-28 00:02:16

Android 多线程断点续传同时下载多个大文件的相关文章

android 多线程断点续传下载 四 - 仿下载助手

我们先一起简单回顾下它的基本原理. http://blog.csdn.net/shimiso/article/details/6763664  android 多线程断点续传下载 一 http://blog.csdn.net/shimiso/article/details/6763986  android 多线程断点续传下载 二 http://blog.csdn.net/shimiso/article/details/8448544  android 多线程断点续传下载 三 界面效果 线程池 T

Android多线程断点续传下载

这个月接到一个项目,要写一个像360助手一样的对于软件管理的APP:其中,遇到了一个问题:多线程断点下载 这个 ,由于之前没有写过这方面的应用功能.所以,不免要自学了.然后就在各个昂站上收索并整理了一下.跟大家分享一下,也能加深我在这方面的理解. 什么是多线程下载? 多线程下载其实就是迅雷,BT一些下载原理,通过多个线程同时和服务器连接,那么你就可以榨取到较高的带宽了,大致做法是将文件切割成N块,每块交给单独一个线程去下载,各自下载完成后将文件块组合成一个文件,程序上要完成做切割和组装的小算法

Android的断点续传的下载在线文件示例

Android的断点续传的下载在线文件示例 文件的结构如下: activity_main.xml: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_h

Android多线程断点续传下载原理及实现

这段时间看了看工作室的工具库的下载组件,发现其存在一些问题: 1.下载核心逻辑有 bug,在暂停下载或下载失败等情况时有概率无法顺利完成下载.2.虽然原来的设计是采用多线程断点续传的设计,但打了一下日志发现其实下载任务都是在同一个线程下串行执行,并没有起到加快下载速度的作用. 考虑到原来的代码并不复杂,因此对这部分下载组件进行了重写.这里记录一下里面的多线程断点续传功能的实现. 请查看完整的PDF版(更多完整项目下载.未完待续.源码.图文知识后续上传github.)可以点击关于我联系我获取完整P

多线程断点续传及下载

断点下载 package com.example.downloaddemo; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import android.os.Bundle; import android.os.Handler; import android.os.

Android多线程断点续传

麻雀虽小五脏俱全,本文我是从慕课网学习的感谢慕课提供了那么免费优秀的资源 这里巧妙的把Android的四大主键紧密的联系了起来 Activity-Service-Broadcast Receiver--SQLite 不多啰嗦 直接上代码 注释很详细 慢慢参透 public class MainActivity extends Activity { private ListView mListView; private List<FileInfo> mFileList; private List

Android应用开发之使用Socket进行大文件断点上传续传

http://www.linuxidc.com/Linux/2012-03/55567.htm http://blog.csdn.net/shimiso/article/details/8529633/ 在Android中上传文件可以采用HTTP方式,也可以采用Socket方式,但是HTTP方式不能上传大文件,这里介绍一种通过Socket方式来进行断点续传的方式,服务端会记录下文件的上传进度,当某一次上传过程意外终止后,下一次可以继续上传,这里用到的其实还是J2SE里的知识. 这个上传程序的原理

Android 多线程断点续传下载

通过HttpURLConnection的setRequestProperty和RandomAccessFile结合使用实现文件多线程下载和断点续传. xml布局: 1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:tools=&quo

基于ibcurl的跨平台多线程断点续传下载库

之前写过一个多线程断点续传的下载库,不过那个是基于一个linux的下载程序.windows下运行还好,android下就各种问题,调试起来还麻烦.后面开发游戏的时候,一方面对下载要求不高,另一方面也精力有限,所以就没有继续研究. 趁现在有时间,我希望实现一个自己满意的下载库,满足以下需求: 1.多线程下载,根据文件大小和下载的文件数目进行调度.一般情况下是一个文件一个文件按照顺序下载,如果文件比较多的情况下可以多个文件同时下载,这个是可以设置的. 2.断点续传.下载进度记录到一个配置文件中,要求