android --多线程下载

来源:互联网 发布:java反射讲四有变公有 编辑:程序博客网 时间:2024/06/01 20:58

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

  (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;      /*** 线程数量<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下载

2 0
原创粉丝点击