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

(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类指定每条线程从本地文件的什么位置开始写入数据。

RandomAccessFilethreadfile= newRandomAccessFile("QQWubiSetup.exe ","rwd");

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

/**  *多线程下载,UI更新类   *@author young  * */  public class MultiThreadDownload extends Thread{      private static final String TAG = "MultiThreadDownload";      /**每一个线程需要下载的大小 */      private int blockSize;      /*** 线程数量<br> 默认为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)  //当前位置小于结束位置  继续下载                      {                          int len = bis.read(buf,0,BUFF_SIZE);                          if(len==-1)   //下载完成                            {                               break;                          }                          rAccessFile.write(buf,0,len);                          curPosition = curPosition +len;                          if(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类进行写入操作。


示例源码:demo下载

更多相关文章

  1. React Native使用高德地图定位(适用Android)
  2. Android(安卓)studio配置Google play服务
  3. React-Native之打包发布(Android)
  4. android Studio 码云新建项目
  5. Android(安卓)4.0.1_r1源代码发布,更新获取方法
  6. javah生成.h文件时提示“找不到类”的解决方法
  7. Android中使用lambda表达式
  8. Mac 配置 gradle
  9. UnsatisfiedLinkError: dalvik.system.PathClassLoader nativeLi

随机推荐

  1. android如果用ListView做一个表格形式
  2. Android 文件存储及常见问题解决
  3. Android中得到view在父容器中的位置下标
  4. 兼容android Q存图片到相册
  5. Android ScrollView滑动到顶部和滑动到底
  6. Android必会小功能总结
  7. 观摩Android最高权力的Context通用性接口
  8. 安卓(Android)九宫格布局介绍
  9. android 异常 Unable to instantiate act
  10. android JNI库实现reboot,recovery