zoukankan      html  css  js  c++  java
  • Android实现网络多线程文件下载

    实现原理

    (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:

      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:

      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, 0, 1024)) != -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

  • 相关阅读:
    Palindrome Partitioning
    triangle
    Populating Next Right Pointers in Each Node(I and II)
    分苹果(网易)
    Flatten Binary Tree to Linked List
    Construct Binary Tree from Inorder and Postorder Traversal(根据中序遍历和后序遍历构建二叉树)
    iOS系统navigationBar背景色,文字颜色处理
    登录,注销
    ios 文字上下滚动效果Demo
    经常崩溃就是数组字典引起的
  • 原文地址:https://www.cnblogs.com/lr393993507/p/4750467.html
Copyright © 2011-2022 走看看