博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
android --多线程下载
阅读量:6605 次
发布时间:2019-06-24

本文共 8572 字,大约阅读时间需要 28 分钟。

多线程下载文件的过程是: 

  (1)首先获得下载文件的长度,然后设置本地文件的长度。

      HttpURLConnection.getContentLength();//获取下载文件的长度

     RandomAccessFile file = new RandomAccessFile("QQSetup.exe","rwd");

       file.setLength(filesize);//设置本地文件的长度

 

  (2)依据文件长度和线程数计算每条线程下载的数据长度和下载位置。

      如:文件的长度为6M,线程数为3,那么。每条线程下载的数据长度为2M,每条线程開始下载的位置例如以下图所看到的。

  

   比如10M大小,使用3个线程来下载,

        线程下载的数据长度   (10%3 == 0 ? 10/3:10/3+1) ,第1,2个线程下载长度是4M,第三个线程下载长度为2M

         下载開始位置:线程id*每条线程下载的数据长度 = ?

        下载结束位置:(线程id+1)*每条线程下载的数据长度-1=?

 

  (3)使用HttpRange头字段指定每条线程从文件的什么位置開始下载,下载到什么位置为止,

         如:指定从文件的2M位置開始下载。下载到位置(4M-1byte)为止

           代码例如以下:HttpURLConnection.setRequestProperty("Range", "bytes=2097152-4194303");

 

  (4)保存文件,使用RandomAccessFile类指定每条线程从本地文件的什么位置開始写入数据。

RandomAccessFile threadfile = new RandomAccessFile("QQWubiSetup.exe ","rwd");

threadfile.seek(2097152);//从文件的什么位置開始写入数据

/**  *多线程下载,UI更新类   *@author young  * */  public class MultiThreadDownload extends Thread{      private static final String TAG = "MultiThreadDownload";      /**每个线程须要下载的大小 */      private int blockSize;      /*** 线程数量
默觉得5个线程下载*/ private int threadNum = 5; /*** 文件大小 */ private int fileSize; /** * 已经下载多少 */ private int downloadSize; /**文件的url,线程编号。文件名*/ private String UrlStr,ThreadNo,fileName; /***保存的路径*/ private String savePath; /**下载的百分比*/ private int downloadPercent = 0; /**下载的 平均速度*/ private int downloadSpeed = 0; /**下载用的时间*/ private int usedTime = 0; /**当前时间*/ private long curTime; /**是否已经下载完毕*/ private boolean completed = false; private Handler handler ; /** * 下载的构造函数 * @param url 请求下载的URL * @param handler UI更新使用 * @param savePath 保存文件的路径 */ public MultiThreadDownload(Handler handler,String url,String savePath) { this.handler = handler; this.UrlStr = url; this.savePath = savePath; Log.e(TAG, toString()); } @Override public void run() { FileDownloadThread[] fds = new FileDownloadThread[threadNum];//设置线程数量 try { URL url = new URL(UrlStr); URLConnection conn = url.openConnection(); fileSize = conn.getContentLength(); this.fileName = FileUtil.getFileName(UrlStr); //仅仅创建一个文件,saveFile下载内容 File saveFile = new File(savePath+"/"+fileName); Log.e(TAG, "文件一共:"+fileSize+" savePath "+savePath+" fileName "+fileName); RandomAccessFile accessFile = new RandomAccessFile(saveFile,"rwd"); //设置本地文件的长度和下载文件同样 accessFile.setLength(fileSize); accessFile.close(); //Handler更新UI,发送消息 sendMsg(FileUtil.startDownloadMeg); //每块线程下载数据 blockSize = ((fileSize%threadNum)==0)?(fileSize/threadNum):(fileSize/threadNum+1); Log.e(TAG, "每个线程分别下载 :"+blockSize); for (int i = 0; i < threadNum; i++) { int curThreadEndPosition = (i+1)!=threadNum ?

((i+1)*blockSize-1) : fileSize; FileDownloadThread fdt = new FileDownloadThread(url, saveFile, i*blockSize, curThreadEndPosition); fdt.setName("thread"+i); fdt.start(); fds[i]=fdt; } /** * 获取数据,更新UI,直到全部下载线程都下载完毕。 */ boolean finished = false; //開始时间,放在循环外,求解的usedTime就是总时间 long startTime = System.currentTimeMillis(); while(!finished) { downloadSize = 0; finished = true; for (int i = 0; i < fds.length; i++) { downloadSize+= fds[i].getDownloadSize(); if(!fds[i].isFinished()) { finished = false; } } downloadPercent = (downloadSize*100)/fileSize; curTime = System.currentTimeMillis(); System.out.println("curTime = "+curTime+" downloadSize = "+downloadSize+" usedTime "+(int) ((curTime-startTime)/1000)); usedTime = (int) ((curTime-startTime)/1000); if(usedTime==0)usedTime = 1; downloadSpeed = (downloadSize/usedTime)/1024; sleep(1000);/*1秒钟刷新一次界面*/ sendMsg(FileUtil.updateDownloadMeg); } Log.e(TAG, "下载完毕"); completed = true; sendMsg(FileUtil.endDownloadMeg); } catch (Exception e) { Log.e(TAG, "multi file error Exception "+e.getMessage()); e.printStackTrace(); } super.run(); } /** * 得到文件的大小 * @return */ public int getFileSize() { return this.fileSize; } /** * 得到已经下载的数量 * @return */ public int getDownloadSize() { return this.downloadSize; } /** * 获取下载百分比 * @return */ public int getDownloadPercent(){ return this.downloadPercent; } /** * 获取下载速度 * @return */ public int getDownloadSpeed(){ return this.downloadSpeed; } /** * 改动默认线程数 * @param threadNum */ public void setThreadNum(int threadNum){ this.threadNum = threadNum; } /** * 分块下载完毕的标志 * @return */ public boolean isCompleted(){ return this.completed; } @Override public String toString() { return "MultiThreadDownload [threadNum=" + threadNum + ", fileSize=" + fileSize + ", UrlStr=" + UrlStr + ", ThreadNo=" + ThreadNo + ", savePath=" + savePath + "]"; } /** * 发送消息。用户提示 * */ private void sendMsg(int what) { Message msg = new Message(); msg.what = what; handler.sendMessage(msg); }

public class FileDownloadThread extends Thread{      private static final String TAG = "FileDownloadThread";      /**缓冲区 */      private static final int BUFF_SIZE = 1024;      /**须要下载的URL*/      private URL url;      /**缓存的FIle*/      private File file;      /**開始位置*/      private int startPosition;      /**结束位置*/      private int endPosition;      /**当前位置*/      private int curPosition;      /**完毕*/      private boolean finished = false;      /**已经下载多少*/      private int downloadSize = 0;            /***      * 分块文件下载,能够创建多线程模式      * @param url   下载的URL      * @param file  下载的文件      * @param startPosition 開始位置      * @param endPosition   结束位置      */      public FileDownloadThread(URL url, File file, int startPosition,              int endPosition) {          this.url = url;          this.file = file;          this.startPosition = startPosition;          this.curPosition = startPosition;          this.endPosition = endPosition;          Log.e(TAG, toString());      }            @Override      public void run() {          BufferedInputStream bis = null;          RandomAccessFile rAccessFile = null;          byte[] buf = new byte[BUFF_SIZE];          URLConnection conn = null;          try {              conn = url.openConnection();              conn.setConnectTimeout(10000);//设置超时              conn.setReadTimeout(10000);              conn.setAllowUserInteraction(true);                      System.out.println(this.getName()+" startPosition "+startPosition+" endPosition "+endPosition);                      conn.setRequestProperty("Range", "bytes="+(startPosition)+"-"+endPosition);  //取剩余未下载的                      rAccessFile = new RandomAccessFile(file,"rwd");//读写                       //设置从什么位置開始写入数据                       rAccessFile.seek(startPosition);                      bis = new BufferedInputStream(conn.getInputStream(), BUFF_SIZE);                      while(curPosition
endPosition) { //假设下载多了。则减去多余部分 System.out.println(" curPosition > endPosition !!!!"); int extraLen = curPosition-endPosition; downloadSize += (len-extraLen+1); }else{ downloadSize+=len; } } this.finished = true; //当前阶段下载完毕 Log.e(TAG, "当前"+this.getName()+"下载完毕"); } catch (Exception e) { Log.e(TAG, "download error Exception "+e.getMessage()); e.printStackTrace(); }finally{ //关闭流 FileUtil.closeInputStream(bis); try { rAccessFile.close(); } catch (IOException e) { // TODO Auto-generated catch block Log.e("AccessFile", "AccessFile IOException "+e.getMessage()); } } super.run(); } /** * 是否完毕当前段下载完毕 * @return */ public boolean isFinished() { return finished; } /** * 已经下载多少 * @return */ public int getDownloadSize() { return downloadSize; } @Override public String toString() { return "FileDownloadThread [url=" + url + ", file=" + file + ", startPosition=" + startPosition + ", endPosition=" + endPosition + ", curPosition=" + curPosition + ", finished=" + finished + ", downloadSize=" + downloadSize + "]"; }
多线程下载是分段下载,创建保存一个文件,子线程分别通过RandomAccessFile类进行写入操作。

演示样例源代码:

转载地址:http://zefso.baihongyu.com/

你可能感兴趣的文章
第一百四十节,JavaScript,封装库--浏览器检测
查看>>
ML2 配置 OVS VxLAN - 每天5分钟玩转 OpenStack(146)
查看>>
ESOURCE_LOCKED - cannot obtain exclusive access to locked queue '2484_0_00163'
查看>>
关于 宏 的一些知识
查看>>
Linux中使用pwconv实现passwd中密码到shadow
查看>>
Kafka – kafka consumer
查看>>
gvim写html代码时如何快速地跳转到一个标签的结束位置: 终极插件: matchit.vim
查看>>
转:NLog 自定义日志内容,写日志到数据库;修改Nlog.config不起作用的原因
查看>>
Java多线程之ReentrantLock重入锁简介与使用教程
查看>>
MongoDB C++ gridfs worked example
查看>>
JSONP
查看>>
Visual Studio 2017各版本安装包离线下载
查看>>
Solidworks如何改变零件颜色
查看>>
C#线程安全的那些事
查看>>
eclipse不会自动编译的问题解决
查看>>
Mysql报错......\xE6\x80\xBB\xE7\x9B\x91' for column...
查看>>
字符串考前总结
查看>>
【论文笔记】Social Role-Aware Emotion Contagion in Image Social Networks
查看>>
2.1.1优化程序性能
查看>>
rpm安装PostgreSQL
查看>>