android 多线程下载 支持断点续传的工具类

来源:互联网 发布:帝国cms建站教程 编辑:程序博客网 时间:2024/05/16 23:40

注释都在代码中,拿下去可以直接当作工具类使用,关于异常的处理都有注释。


  1. import java.io.File;  
  2. import java.io.IOException;  
  3. import java.io.InputStream;  
  4. import java.io.RandomAccessFile;  
  5. import java.net.HttpURLConnection;  
  6. import java.net.URL;  
  7.   
  8. import android.content.Context;  
  9. import android.os.Environment;  
  10. import android.os.Handler;  
  11. import android.os.Message;  
  12.   
  13. import com.easipass.tool.weather.NetUtils;  
  14.   
  15. /*** 
  16.  * 实现多线程断点下载 
  17.  *  
  18.  */  
  19. public class BreakpointDownloader {  
  20.     private static final String DIR_PATH = Environment.getExternalStorageDirectory() + "/"// 下载目录  
  21.     private static final int THREAD_AMOUNT = 3;             // 总线程数  
  22.       
  23.     private URL url;            // 目标下载地址  
  24.     private File dataFile;      // 本地文件  
  25.     private File tempFile;      // 用来存储每个线程下载的进度的临时文件  
  26.     private long threadLen;     // 每个线程要下载的长度  
  27.     private long totalFinish;   // 总共完成了多少  
  28.     private long totalLen;      // 服务端文件总长度  
  29.     public static long backtotalLen;  
  30.     private long begin;         // 用来记录开始下载时的时间  
  31.     private Handler handler;  
  32.     private Context context;  
  33.     private boolean allthreadisrun = true;  
  34.   
  35.     public BreakpointDownloader(int newCode, String address, Handler handler,Context context) throws IOException {    
  36.         url = new URL(address);                                                         // 记住下载地址  
  37.         dataFile = new File(DIR_PATH, Config.UPDATE_SAVENAME.replace(".apk", newCode + ".apk"));    // 截取地址中的文件名, 创建本地文件  
  38.         tempFile = new File(dataFile.getAbsolutePath() + ".temp");                      // 在本地文件所在文件夹中创建临时文件  
  39.         this.context = context;  
  40.   
  41.         File oldDataFile = new File(DIR_PATH, Config.UPDATE_SAVENAME.replace(".apk", newCode-1 + ".apk"));  
  42.         File oldTempFile = new File(oldDataFile.getAbsolutePath() + ".temp");      
  43.         if(oldDataFile.exists())  
  44.             oldDataFile.delete();  
  45.         if(oldTempFile.exists())  
  46.             oldTempFile.delete();  
  47.         this.handler = handler;  
  48.     }  
  49.   
  50.     public void download() throws IOException {       
  51.         HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
  52.         conn.setConnectTimeout(10000);    
  53.         totalLen = conn.getContentLength();                                 // 获取服务端发送过来的文件长度  
  54.         threadLen = (totalLen + THREAD_AMOUNT - 1) / THREAD_AMOUNT;         // 计算每个线程要下载的长度  
  55.   
  56.         Message msg = new Message();      
  57.         if(totalLen<0){  
  58.             msg.getData().putLong("totalLen", backtotalLen);      
  59.         }else{  
  60.             backtotalLen = totalLen;  
  61.             msg.getData().putLong("totalLen", totalLen);          
  62.         }  
  63.   
  64.   
  65.         msg.what = 1;  
  66.         handler.sendMessage(msg);                                           // 发送文件总长度  
  67.           
  68.         if (!dataFile.exists()) {                                           // 如果本地文件不存在  
  69.         try{  
  70.             RandomAccessFile raf = new RandomAccessFile(dataFile, "rws");   // 在本地创建文件  
  71.             raf.setLength(totalLen);                                        // 设置文件的大小和服务端相同  
  72.             raf.close();  
  73.             }catch(Exception e){  
  74.                 e.printStackTrace();  
  75.             }  
  76.         }         
  77.           
  78.         if (!tempFile.exists()) {                                           // 如果临时文件不存在  
  79.             RandomAccessFile raf = new RandomAccessFile(tempFile, "rws");   // 创建临时文件, 用来记录每个线程已下载多少  
  80.             for (int i = 0; i < THREAD_AMOUNT; i++)                          // 按照线程数循环  
  81.                 raf.writeLong(0);                                           // 写入每个线程的开始位置(都是从0开始)  
  82.             raf.close();  
  83.         }  
  84.           
  85.         for (int i = 0; i < THREAD_AMOUNT; i++)  // 按照线程数循环  
  86.             new DownloadThread(i).start();      // 开启线程, 每个线程将会下载一部分数据到本地文件中  
  87.           
  88.         begin = System.currentTimeMillis();     // 记录开始时间  
  89.     }  
  90.       
  91.     private class DownloadThread extends Thread {  
  92.         private int id;     // 用来标记当前线程是下载任务中的第几个线程  
  93.           
  94.         public DownloadThread(int id) {  
  95.             this.id = id;  
  96.         }  
  97.         boolean flag = true;  
  98.           
  99.         public void run() {  
  100.             boolean isrun = false;  //每个线程都有自己的标识位,用来控制线程的重启  
  101.             while(allthreadisrun && !isrun){  
  102.                 //判断网络连接是否可用,不可用直接退出  
  103.                 if (!NetUtils.getNetworkIsAvailable(context)) {  
  104.                     handler.sendEmptyMessage(3);  
  105.                     return;  
  106.                 }  
  107.                 try {  
  108.                     isrun = true;  
  109.                     if(tempFile.exists()&&dataFile.exists()){  
  110.                         RandomAccessFile tempRaf = new RandomAccessFile(tempFile, "rws");       // 用来记录下载进度的临时文件  
  111.                         tempRaf.seek(id * 8);                       // 将指针移动到当前线程的位置(每个线程写1个long值, 占8字节)  
  112.                         long threadFinish = tempRaf.readLong();     // 读取当前线程已完成了多少  
  113.                         //判断 如果当前线程已经完成了下载,则退出该线程  
  114.                         if(threadFinish == threadLen){  
  115. //                          System.out.println("线程"+id+"已经退出");  
  116.                             tempRaf.close();  
  117.                             totalFinish +=threadFinish;   
  118.                             if (totalFinish >= totalLen && totalFinish == (totalLen + THREAD_AMOUNT - 1)) {                  // 如果已完成长度等于服务端文件长度(代表下载完成)  
  119.                                 System.out.println("下载完成, 耗时: " + (System.currentTimeMillis() - begin));  
  120.                                 tempFile.delete();                          // 删除临时文件  
  121.                             }   
  122.                             return;  
  123.                         }  
  124.                         synchronized(BreakpointDownloader.this) {   // 多个下载线程之间同步  
  125.                             totalFinish += threadFinish;            // 统计所有线程总共完成了多少  
  126.                         }  
  127.                       
  128. //                      System.out.println("totalFinish="+totalFinish);  
  129.                         Message msg = new Message();  
  130.                         msg.getData().putLong("totalFinish", totalFinish);  
  131.                         msg.what = 2;  
  132.                         handler.sendMessage(msg);             
  133.                           
  134.                           
  135.                         long start = id * threadLen + threadFinish;     // 计算当前线程的起始位置  
  136.                         long end = id * threadLen + threadLen - 1;      // 计算当前线程的结束位置  
  137.                         System.out.println("线程" + id + ": " + start + "-" + end);  
  138.                       
  139. //                      System.out.println("------->url"+url);  
  140.                         HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
  141.                         conn.setConnectTimeout(10000);  
  142.                         conn.setRequestProperty("Range""bytes=" + start + "-" + end);     // 设置当前线程下载的范围  
  143.                           
  144.                         InputStream in = conn.getInputStream();                             // 获取连接的输入流  
  145.                         RandomAccessFile dataRaf = new RandomAccessFile(dataFile, "rws");   // 装载数据的本地文件(可以理解为输出流)  
  146.                         dataRaf.seek(start);                                                // 设置当前线程保存数据的位置  
  147.                           
  148.                         byte[] buffer = new byte[1024 * 100];           // 每次拷贝100KB  
  149.                         int len;  
  150.                         while (totalFinish<totalLen && (len = in.read(buffer)) != -1) {  
  151.                             dataRaf.write(buffer, 0, len);              // 从服务端读取数据, 写到本地文件  
  152.                             threadFinish += len;                        // 每次写入数据之后, 统计当前线程完成了多少  
  153.                             tempRaf.seek(id * 8);                       // 将临时文件的指针指向当前线程的位置  
  154.                             tempRaf.writeLong(threadFinish);            // 将当前线程完成了多少写入到临时文件  
  155.                             synchronized(BreakpointDownloader.this) {   // 多个下载线程之间同步  
  156.                                 totalFinish += len;                     // 统计所有线程总共完成了多少  
  157.                                 Message msg3 = new Message();  
  158.                                 msg3.getData().putLong("totalFinish", totalFinish);  
  159.                                 msg3.what = 2;  
  160.                                 handler.sendMessage(msg3);              // 发送当前进度  
  161. //                              System.out.println("下载进度-----》"+totalFinish+"/"+totalLen);  
  162.                             }  
  163.                         }  
  164.                         dataRaf.close();  
  165.                         tempRaf.close();  
  166.                     }  
  167.                 } catch (IOException e) {                     
  168.                     e.printStackTrace();  
  169. //                  System.out.println("1 ---》这里异常了");  
  170.                       
  171.                     if (!NetUtils.getNetworkIsAvailable(context)) {  
  172. //                      System.out.println("已经发送消息");  
  173.                         handler.sendEmptyMessage(3);  
  174.                         return;  
  175.                     }  
  176.                     try {  
  177.                         isrun = true;  
  178.                         if(tempFile.exists()&&dataFile.exists()){  
  179.                             RandomAccessFile tempRaf = new RandomAccessFile(tempFile, "rws");       // 用来记录下载进度的临时文件  
  180.                             tempRaf.seek(id * 8);                       // 将指针移动到当前线程的位置(每个线程写1个long值, 占8字节)  
  181.                             long threadFinish = tempRaf.readLong();     // 读取当前线程已完成了多少  
  182.                             //判断 如果当前线程已经完成了下载,则退出该线程  
  183.                             if(threadFinish == threadLen){  
  184.                                 System.out.println("线程"+id+"已经退出");  
  185.                                 tempRaf.close();  
  186.                                 totalFinish +=threadFinish;   
  187.                                 if (totalFinish >= totalLen && totalFinish == (totalLen + THREAD_AMOUNT - 1)) {                  // 如果已完成长度等于服务端文件长度(代表下载完成)  
  188.                                     System.out.println("下载完成, 耗时: " + (System.currentTimeMillis() - begin));  
  189.                                     tempFile.delete();                          // 删除临时文件  
  190.                                 }   
  191.                                 return;  
  192.                             }  
  193.                             synchronized(BreakpointDownloader.this) {   // 多个下载线程之间同步  
  194.                                 totalFinish += threadFinish;            // 统计所有线程总共完成了多少  
  195.                             }  
  196.                               
  197.                             long start = id * threadLen + threadFinish;     // 计算当前线程的起始位置  
  198.                             long end = id * threadLen + threadLen - 1;      // 计算当前线程的结束位置  
  199.                             System.out.println("线程" + id + ": " + start + "-" + end);  
  200.                           
  201.                             HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
  202.                             conn.setConnectTimeout(10000);  
  203.                             conn.setRequestProperty("Range""bytes=" + start + "-" + end);     // 设置当前线程下载的范围  
  204.                               
  205.                             InputStream in = conn.getInputStream();                             // 获取连接的输入流  
  206.                             RandomAccessFile dataRaf = new RandomAccessFile(dataFile, "rws");   // 装载数据的本地文件(可以理解为输出流)  
  207.                             dataRaf.seek(start);                                                // 设置当前线程保存数据的位置  
  208.                               
  209.                             byte[] buffer = new byte[1024 * 100];           // 每次拷贝100KB  
  210.                             int len;  
  211.                             while (totalFinish<totalLen && (len = in.read(buffer)) != -1) {  
  212.                                 dataRaf.write(buffer, 0, len);              // 从服务端读取数据, 写到本地文件  
  213.                                 threadFinish += len;                        // 每次写入数据之后, 统计当前线程完成了多少  
  214.                                 tempRaf.seek(id * 8);                       // 将临时文件的指针指向当前线程的位置  
  215.                                 tempRaf.writeLong(threadFinish);            // 将当前线程完成了多少写入到临时文件  
  216.                                 synchronized(BreakpointDownloader.this) {   // 多个下载线程之间同步  
  217.                                     totalFinish += len;                     // 统计所有线程总共完成了多少  
  218.                                     Message msg4 = new Message();  
  219.                                     msg4.getData().putLong("totalFinish", totalFinish);  
  220.                                     msg4.what = 2;  
  221.                                     handler.sendMessage(msg4);              // 发送当前进度  
  222. //                                  System.out.println("下载进度-----》"+totalFinish+"/"+totalLen);  
  223.                                 }  
  224.                             }  
  225.                             dataRaf.close();  
  226.                             tempRaf.close();  
  227.                         }                 
  228.                       
  229.                     }catch(Exception e2){                         
  230.                         e2.printStackTrace();  
  231.                         isrun = false;  
  232.                         System.out.println("2 ---》这里异常了");  
  233.                         if(flag) {  
  234.                             flag = false;  
  235.                             handler.sendEmptyMessage(3);  
  236.                             allthreadisrun = false;  
  237.                         }                             
  238.                     }  
  239.                       
  240.                 }  
  241.   
  242.             }  
  243.                   
  244.                 if (totalFinish == totalLen || totalFinish == (totalLen + THREAD_AMOUNT - 1)) {                 // 如果已完成长度等于服务端文件长度(代表下载完成)  
  245.                     System.out.println("下载完成, 耗时: " + (System.currentTimeMillis() - begin));  
  246.                     tempFile.delete();                          // 删除临时文件  
  247.                 }else if(dataFile.length()<totalLen){  
  248.                     //未下载完成  
  249.                     System.out.println("下载未完成");  
  250.                     allthreadisrun = false;  
  251.                     handler.sendEmptyMessage(3);                  
  252.                 }  
  253.   
  254.         }  
  255.     }  
  256. }