Android实现网络多线程文件下载

来源:互联网 发布:oa数据库架构 编辑:程序博客网 时间:2024/06/05 16:49

实现原理

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

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

如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如下图所示:

(网上找的图)

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

线程下载的数据长度   (10%3 == 0 ? 10/3:10/3+1) ,第1,2个线程下载长度是4M,第三个线程下载长度为2M
下载开始位置:线程id*每条线程下载的数据长度 = ?
下载结束位置:(线程id+1)*每条线程下载的数据长度-1=?


之前练习时的一个demo,不多说了,直接上代码吧,有关断点续传,需要使用数据库,不再加了,网上有很多成熟的项目可以直接用。



实例

MainApp:

[java] view plaincopy
  1. package com.amos.app;  
  2.   
  3. import java.io.File;  
  4. import java.io.IOException;  
  5. import java.net.MalformedURLException;  
  6. import java.net.URL;  
  7. import java.net.URLConnection;  
  8. import com.amos.download.R;  
  9. import android.annotation.SuppressLint;  
  10. import android.app.Activity;  
  11. import android.os.Bundle;  
  12. import android.os.Environment;  
  13. import android.os.Handler;  
  14. import android.os.Message;  
  15. import android.util.Log;  
  16. import android.view.View;  
  17. import android.view.View.OnClickListener;  
  18. import android.widget.ProgressBar;  
  19. import android.widget.TextView;  
  20. import android.widget.Toast;  
  21.   
  22. /** 
  23.  * @author yangxiaolong 
  24.  * @2014-5-6 
  25.  */  
  26. public class MainApp extends Activity implements OnClickListener {  
  27.   
  28.     private static final String TAG = MainApp.class.getSimpleName();  
  29.   
  30.     /** 显示下载进度TextView */  
  31.     private TextView mMessageView;  
  32.     /** 显示下载进度ProgressBar */  
  33.     private ProgressBar mProgressbar;  
  34.   
  35.     @Override  
  36.     protected void onCreate(Bundle savedInstanceState) {  
  37.         super.onCreate(savedInstanceState);  
  38.         setContentView(R.layout.progress_activity);  
  39.         findViewById(R.id.download_btn).setOnClickListener(this);  
  40.         mMessageView = (TextView) findViewById(R.id.download_message);  
  41.         mProgressbar = (ProgressBar) findViewById(R.id.download_progress);  
  42.     }  
  43.   
  44.     @Override  
  45.     public void onClick(View v) {  
  46.         if (v.getId() == R.id.download_btn) {  
  47.             doDownload();  
  48.         }  
  49.     }  
  50.   
  51.     /** 
  52.      * 使用Handler更新UI界面信息 
  53.      */  
  54.     @SuppressLint("HandlerLeak")  
  55.     Handler mHandler = new Handler() {  
  56.         @Override  
  57.         public void handleMessage(Message msg) {  
  58.   
  59.             mProgressbar.setProgress(msg.getData().getInt("size"));  
  60.   
  61.             float temp = (float) mProgressbar.getProgress()  
  62.                     / (float) mProgressbar.getMax();  
  63.   
  64.             int progress = (int) (temp * 100);  
  65.             if (progress == 100) {  
  66.                 Toast.makeText(MainApp.this"下载完成!", Toast.LENGTH_LONG).show();  
  67.             }  
  68.             mMessageView.setText("下载进度:" + progress + " %");  
  69.   
  70.         }  
  71.     };  
  72.   
  73.     /** 
  74.      * 下载准备工作,获取SD卡路径、开启线程 
  75.      */  
  76.     private void doDownload() {  
  77.         // 获取SD卡路径  
  78.         String path = Environment.getExternalStorageDirectory()  
  79.                 + "/amosdownload/";  
  80.         File file = new File(path);  
  81.         // 如果SD卡目录不存在创建  
  82.         if (!file.exists()) {  
  83.             file.mkdir();  
  84.         }  
  85.         // 设置progressBar初始化  
  86.         mProgressbar.setProgress(0);  
  87.   
  88.         // 简单起见,我先把URL和文件名称写死,其实这些都可以通过HttpHeader获取到  
  89.         String downloadUrl = "http://gdown.baidu.com/data/wisegame/91319a5a1dfae322/baidu_16785426.apk";  
  90.         String fileName = "baidu_16785426.apk";  
  91.         int threadNum = 5;  
  92.         String filepath = path + fileName;  
  93.         Log.d(TAG, "download file  path:" + filepath);  
  94.         downloadTask task = new downloadTask(downloadUrl, threadNum, filepath);  
  95.         task.start();  
  96.     }  
  97.   
  98.     /** 
  99.      * 多线程文件下载 
  100.      *  
  101.      * @author yangxiaolong 
  102.      * @2014-8-7 
  103.      */  
  104.     class downloadTask extends Thread {  
  105.         private String downloadUrl;// 下载链接地址  
  106.         private int threadNum;// 开启的线程数  
  107.         private String filePath;// 保存文件路径地址  
  108.         private int blockSize;// 每一个线程的下载量  
  109.   
  110.         public downloadTask(String downloadUrl, int threadNum, String fileptah) {  
  111.             this.downloadUrl = downloadUrl;  
  112.             this.threadNum = threadNum;  
  113.             this.filePath = fileptah;  
  114.         }  
  115.   
  116.         @Override  
  117.         public void run() {  
  118.   
  119.             FileDownloadThread[] threads = new FileDownloadThread[threadNum];  
  120.             try {  
  121.                 URL url = new URL(downloadUrl);  
  122.                 Log.d(TAG, "download file http path:" + downloadUrl);  
  123.                 URLConnection conn = url.openConnection();  
  124.                 // 读取下载文件总大小  
  125.                 int fileSize = conn.getContentLength();  
  126.                 if (fileSize <= 0) {  
  127.                     System.out.println("读取文件失败");  
  128.                     return;  
  129.                 }  
  130.                 // 设置ProgressBar最大的长度为文件Size  
  131.                 mProgressbar.setMax(fileSize);  
  132.   
  133.                 // 计算每条线程下载的数据长度  
  134.                 blockSize = (fileSize % threadNum) == 0 ? fileSize / threadNum  
  135.                         : fileSize / threadNum + 1;  
  136.   
  137.                 Log.d(TAG, "fileSize:" + fileSize + "  blockSize:");  
  138.   
  139.                 File file = new File(filePath);  
  140.                 for (int i = 0; i < threads.length; i++) {  
  141.                     // 启动线程,分别下载每个线程需要下载的部分  
  142.                     threads[i] = new FileDownloadThread(url, file, blockSize,  
  143.                             (i + 1));  
  144.                     threads[i].setName("Thread:" + i);  
  145.                     threads[i].start();  
  146.                 }  
  147.   
  148.                 boolean isfinished = false;  
  149.                 int downloadedAllSize = 0;  
  150.                 while (!isfinished) {  
  151.                     isfinished = true;  
  152.                     // 当前所有线程下载总量  
  153.                     downloadedAllSize = 0;  
  154.                     for (int i = 0; i < threads.length; i++) {  
  155.                         downloadedAllSize += threads[i].getDownloadLength();  
  156.                         if (!threads[i].isCompleted()) {  
  157.                             isfinished = false;  
  158.                         }  
  159.                     }  
  160.                     // 通知handler去更新视图组件  
  161.                     Message msg = new Message();  
  162.                     msg.getData().putInt("size", downloadedAllSize);  
  163.                     mHandler.sendMessage(msg);  
  164.                     // Log.d(TAG, "current downloadSize:" + downloadedAllSize);  
  165.                     Thread.sleep(1000);// 休息1秒后再读取下载进度  
  166.                 }  
  167.                 Log.d(TAG, " all of downloadSize:" + downloadedAllSize);  
  168.   
  169.             } catch (MalformedURLException e) {  
  170.                 e.printStackTrace();  
  171.             } catch (IOException e) {  
  172.                 e.printStackTrace();  
  173.             } catch (InterruptedException e) {  
  174.                 e.printStackTrace();  
  175.             }  
  176.   
  177.         }  
  178.     }  
  179.   
  180. }  


FileDownloadThread:

[java] view plaincopy
  1. package com.amos.app;  
  2.   
  3. import java.io.BufferedInputStream;  
  4. import java.io.File;  
  5. import java.io.IOException;  
  6. import java.io.RandomAccessFile;  
  7. import java.net.URL;  
  8. import java.net.URLConnection;  
  9. import android.util.Log;  
  10.   
  11. /** 
  12.  * 文件下载类 
  13.  *  
  14.  * @author yangxiaolong 
  15.  * @2014-5-6 
  16.  */  
  17. public class FileDownloadThread extends Thread {  
  18.   
  19.     private static final String TAG = FileDownloadThread.class.getSimpleName();  
  20.   
  21.     /** 当前下载是否完成 */  
  22.     private boolean isCompleted = false;  
  23.     /** 当前下载文件长度 */  
  24.     private int downloadLength = 0;  
  25.     /** 文件保存路径 */  
  26.     private File file;  
  27.     /** 文件下载路径 */  
  28.     private URL downloadUrl;  
  29.     /** 当前下载线程ID */  
  30.     private int threadId;  
  31.     /** 线程下载数据长度 */  
  32.     private int blockSize;  
  33.   
  34.     /** 
  35.      *  
  36.      * @param url:文件下载地址 
  37.      * @param file:文件保存路径 
  38.      * @param blocksize:下载数据长度 
  39.      * @param threadId:线程ID 
  40.      */  
  41.     public FileDownloadThread(URL downloadUrl, File file, int blocksize,  
  42.             int threadId) {  
  43.         this.downloadUrl = downloadUrl;  
  44.         this.file = file;  
  45.         this.threadId = threadId;  
  46.         this.blockSize = blocksize;  
  47.     }  
  48.   
  49.     @Override  
  50.     public void run() {  
  51.   
  52.         BufferedInputStream bis = null;  
  53.         RandomAccessFile raf = null;  
  54.   
  55.         try {  
  56.             URLConnection conn = downloadUrl.openConnection();  
  57.             conn.setAllowUserInteraction(true);  
  58.   
  59.             int startPos = blockSize * (threadId - 1);//开始位置  
  60.             int endPos = blockSize * threadId - 1;//结束位置  
  61.             //设置当前线程下载的起点、终点  
  62.             conn.setRequestProperty("Range""bytes=" + startPos + "-" + endPos);  
  63.             System.out.println(Thread.currentThread().getName() + "  bytes="  
  64.                     + startPos + "-" + endPos);  
  65.   
  66.             byte[] buffer = new byte[1024];  
  67.             bis = new BufferedInputStream(conn.getInputStream());  
  68.   
  69.             raf = new RandomAccessFile(file, "rwd");  
  70.             raf.seek(startPos);  
  71.             int len;  
  72.             while ((len = bis.read(buffer, 01024)) != -1) {  
  73.                 raf.write(buffer, 0, len);  
  74.                 downloadLength += len;  
  75.             }  
  76.             isCompleted = true;  
  77.             Log.d(TAG, "current thread task has finished,all size:"  
  78.                     + downloadLength);  
  79.   
  80.         } catch (IOException e) {  
  81.             e.printStackTrace();  
  82.         } finally {  
  83.             if (bis != null) {  
  84.                 try {  
  85.                     bis.close();  
  86.                 } catch (IOException e) {  
  87.                     e.printStackTrace();  
  88.                 }  
  89.             }  
  90.             if (raf != null) {  
  91.                 try {  
  92.                     raf.close();  
  93.                 } catch (IOException e) {  
  94.                     e.printStackTrace();  
  95.                 }  
  96.             }  
  97.         }  
  98.     }  
  99.   
  100.     /** 
  101.      * 线程文件是否下载完毕 
  102.      */  
  103.     public boolean isCompleted() {  
  104.         return isCompleted;  
  105.     }  
  106.   
  107.     /** 
  108.      * 线程下载文件长度 
  109.      */  
  110.     public int getDownloadLength() {  
  111.         return downloadLength;  
  112.     }  
  113.   
  114. }  


效果图:


Log控制台:


可以看到文件总大小、我们创建的5个线程每个负责下载的区间


SD卡:




断点续传

这个用到了数据存储保存当前每个线程下载文件的长度,等下一次再下载时读取,网上有成熟的案例,就不再造轮子了,资源里我打包了自己的项目和带断点续传的项目(别人的),大家可以下载下来直接用,全部免费:

http://download.csdn.net/detail/mad1989/7727133

0 0
原创粉丝点击