• 文件多线程下载实现


    布局文件

     1 - <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity">
     2 - <LinearLayout android:id="@+id/ll" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal">
     3 - <EditText android:id="@+id/pathET" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:inputType="textUri" android:text="http://192.168.1.251:8080/WebServer/FeiQ.exe">
     4   <requestFocus /> 
     5   </EditText>
     6   <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="download" android:src="@android:drawable/ic_media_play" /> 
     7   </LinearLayout>
     8   <ProgressBar android:id="@+id/downloadPB" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/ll" android:layout_marginTop="20dp" /> 
     9   <TextView android:id="@+id/percentTV" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/downloadPB" android:text="0%" /> 
    10   <TextView android:id="@+id/progressTV" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_below="@id/downloadPB" android:text="0/0" /> 
    11   </RelativeLayout>
    View Code

    MyHandler.java

     1 package com.itheima.download;
     2 
     3 import java.io.File;
     4 import java.io.FileOutputStream;
     5 import java.io.IOException;
     6 import java.io.InputStream;
     7 
     8 import org.apache.http.HttpEntity;
     9 
    10 import android.content.Context;
    11 
    12 import com.loopj.android.http.FileAsyncHttpResponseHandler;
    13 
    14 public abstract class MyFileHandler extends FileAsyncHttpResponseHandler {
    15     private boolean isCancel;
    16 
    17     public MyFileHandler(Context context) {
    18         super(context);
    19     }
    20 
    21     public MyFileHandler(File file) {
    22         super(file);
    23     }
    24 
    25     @Override
    26     protected byte[] getResponseData(HttpEntity entity) throws IOException {
    27         if (entity != null) {
    28             InputStream instream = entity.getContent();
    29             long contentLength = entity.getContentLength();
    30             FileOutputStream buffer = new FileOutputStream(getTargetFile(), true);
    31             if (instream != null) {
    32                 try {
    33                     byte[] tmp = new byte[BUFFER_SIZE];
    34                     int l, count = 0;
    35                     // do not send messages if request has been cancelled
    36                     while ((l = instream.read(tmp)) != -1 && !Thread.currentThread().isInterrupted()) {
    37                         if (isCancel)
    38                             break;
    39                         count += l;
    40                         buffer.write(tmp, 0, l);
    41                         sendProgressMessage(count, (int) contentLength);
    42                     }
    43                 } finally {
    44                     instream.close();
    45                     buffer.flush();
    46                     buffer.close();
    47                 }
    48             }
    49         }
    50         return null;
    51     }
    52     
    53     public void cancel() {
    54         isCancel = true;
    55     }
    56 
    57 }
    View Code

    MainActivity.java

     1 package com.itheima.download;
     2 
     3 import java.io.File;
     4 
     5 import android.app.Activity;
     6 import android.os.Bundle;
     7 import android.os.Environment;
     8 import android.view.View;
     9 import android.widget.EditText;
    10 import android.widget.ImageButton;
    11 import android.widget.ProgressBar;
    12 import android.widget.TextView;
    13 
    14 public class MainActivity extends Activity {
    15 
    16 //    private AsyncHttpClient client;
    17     
    18     private EditText pathET;
    19     private ProgressBar downloadPB;
    20     private TextView percentTV;
    21     private TextView progressTV;
    22     private DownloadTask task;
    23 
    24     @Override
    25     protected void onCreate(Bundle savedInstanceState) {
    26         super.onCreate(savedInstanceState);
    27         setContentView(R.layout.activity_main);
    28         
    29         pathET = (EditText) findViewById(R.id.pathET);
    30         downloadPB = (ProgressBar) findViewById(R.id.downloadPB);
    31         percentTV = (TextView) findViewById(R.id.percentTV);
    32         progressTV = (TextView) findViewById(R.id.progressTV);
    33         
    34 //        client = new AsyncHttpClient();
    35     }
    36     
    37     public void download(View v) {
    38         ImageButton ib = (ImageButton) v;
    39         
    40         if (task == null) {
    41             String targetUrl = pathET.getText().toString().trim();
    42             File localFile = new File(Environment.getExternalStorageDirectory(), targetUrl.substring(targetUrl.lastIndexOf("/") + 1));
    43             task = new DownloadTask(targetUrl, localFile, 3, this, downloadPB, percentTV, progressTV);
    44             task.execute();
    45             ib.setImageResource(android.R.drawable.ic_media_pause);
    46         } else {
    47             task.stop();
    48             task = null;
    49             ib.setImageResource(android.R.drawable.ic_media_play);
    50         }
    51     }
    52 
    53     /*
    54     public void classicDownload(View v) {
    55         String path = pathET.getText().toString().trim();
    56         File file = new File("/mnt/sdcard/", path.substring(path.lastIndexOf("/") + 1));
    57         
    58         client.get(path, new FileAsyncHttpResponseHandler(file) {        // 处理文件传输的处理器
    59             public void onSuccess(int statusCode, Header[] headers, File file) {
    60                 Toast.makeText(getApplicationContext(), "下载成功", Toast.LENGTH_SHORT).show();
    61             }
    62             public void onFailure(int statusCode, Header[] headers, Throwable throwable, File file) {
    63                 Toast.makeText(getApplicationContext(), "下载失败 " + statusCode, Toast.LENGTH_SHORT).show();
    64                 throwable.printStackTrace();
    65             }
    66             public void onProgress(int bytesWritten, int totalSize) {    // 下载或上传的进度
    67                 System.out.println(bytesWritten + " / " + totalSize + " (" + bytesWritten * 100 / totalSize + "%)");
    68             }
    69         });
    70     }
    71     */
    72 
    73 }
    View Code

    DownloadTask.java

      1 package com.itheima.download;
      2 
      3 import java.io.File;
      4 import java.io.FileInputStream;
      5 import java.io.FileOutputStream;
      6 import java.io.IOException;
      7 import java.util.ArrayList;
      8 
      9 import org.apache.http.Header;
     10 
     11 import android.content.Context;
     12 import android.widget.ProgressBar;
     13 import android.widget.TextView;
     14 
     15 import com.loopj.android.http.AsyncHttpClient;
     16 import com.loopj.android.http.AsyncHttpResponseHandler;
     17 
     18 public class DownloadTask {
     19     private String targetUrl;
     20     private File localFile;
     21     private int threadAmount;
     22     private Context context;
     23     private ProgressBar downloadPB;
     24     private TextView percentTV;
     25     private TextView progressTV;
     26 
     27     private long contentLength;
     28     private long threadLength;
     29     private ArrayList<File> cacheList = new ArrayList<File>();
     30     private ArrayList<MyFileHandler> handlerList = new ArrayList<MyFileHandler>();
     31     
     32     /**
     33      * 创建下载任务
     34      * @param targetUrl        目标地址
     35      * @param localFile        本地路径
     36      * @param threadAmount    线程数量
     37      * @param context        应用环境
     38      * @param downloadPB 
     39      * @param progressTV 
     40      * @param percentTV 
     41      */
     42     public DownloadTask(String targetUrl, File localFile, int threadAmount, Context context, ProgressBar downloadPB, TextView percentTV, TextView progressTV) {
     43         super();
     44         this.targetUrl = targetUrl;
     45         this.localFile = localFile;
     46         this.threadAmount = threadAmount;
     47         this.context = context;
     48         this.downloadPB = downloadPB;
     49         this.percentTV = percentTV;
     50         this.progressTV = progressTV;
     51     }
     52 
     53     public void execute() {
     54         new AsyncHttpClient().head(targetUrl, new AsyncHttpResponseHandler() {        // 根据URL向服务器发起一个请求, 获取响应头
     55             public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
     56                 caculateLength(headers);    // 获取文件总长度, 计算每个线程负责的长度
     57                 beginDownload();            // 开启多个线程下载
     58             }
     59             public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
     60             }
     61         });
     62     }
     63     
     64     private void caculateLength(Header[] headers) {        
     65         for (Header header : headers) 
     66             if ("Content-Length".equals(header.getName())) 
     67                 contentLength = Long.parseLong(header.getValue());            // 从相应头中获取文件总大小
     68         threadLength = (contentLength + threadAmount - 1) / threadAmount;    // 计算每个线程负责多少
     69         downloadPB.setMax((int) contentLength);        // 设置进度条的最大刻度为文件总大小
     70     }
     71     
     72     private void beginDownload() {
     73         for (int i = 0; i < threadAmount; i++) {    // 定义循环, 每次循环启动1个线程下载
     74             File cacheFile = new File(context.getCacheDir(), localFile.getName() + ".temp" + i);    // 定义临时文件的路径
     75             cacheList.add(cacheFile);                // 把文件装入集合
     76             
     77             long begin = i * threadLength + cacheFile.length();        // 计算开始位置
     78             long end = i * threadLength + threadLength - 1;            // 计算结束位置
     79             System.out.println(i + ": " + begin + " - " + end);
     80             if (begin > end)
     81                 continue;
     82             
     83             MyFileHandler fileHandler = new MyFileHandler(cacheFile) {            // 发送请求, 下载数据, 存储到临时文件
     84                 private long ms;
     85                 public void onSuccess(int statusCode, Header[] headers, File file) {
     86                     System.out.println(Thread.currentThread().getName() + ": " + file + " 下载完成");
     87                     checkOtherThread();                // 检查其他线程是否下载完毕, 如果都完了, 合并文件
     88                 }
     89                 public void onFailure(int statusCode, Header[] headers, Throwable throwable, File file) {
     90                 }
     91                 public void onProgress(int bytesWritten, int totalSize) {    // 运行频率非常高
     92                     if (System.currentTimeMillis() - ms > 100) {
     93                         ms = System.currentTimeMillis();
     94                         updateProgress();
     95                     }
     96                 }
     97             };
     98             handlerList.add(fileHandler);
     99             
    100             AsyncHttpClient client = new AsyncHttpClient();
    101             client.addHeader("Range", "bytes=" + begin + "-" + end);        // 设置请求数据的范围
    102             client.get(targetUrl, fileHandler);
    103         }
    104     }
    105     
    106     private void updateProgress() {
    107         long finishLength = getFinishLength();
    108         if (finishLength == 0)
    109             return;
    110         downloadPB.setProgress((int) finishLength);
    111         percentTV.setText(finishLength * 1000 / contentLength / 10f + "%"); 
    112         progressTV.setText(finishLength + "/" + contentLength);
    113     }
    114     
    115     private long getFinishLength() {
    116         long finishLength = 0;
    117         for (File cacheFile : cacheList)             // 遍历所有临时文件
    118             finishLength += cacheFile.length();        // 统计总长度
    119         return finishLength;
    120     }
    121 
    122     private void checkOtherThread() {
    123         if (getFinishLength() == contentLength) {     // 如果文件长度和ContentLength相等, 代表下载完成
    124             updateProgress();
    125             merge();                                // 合并所有的临时文件
    126         }
    127     }
    128 
    129     private void merge() {
    130         try {
    131             FileOutputStream out = new FileOutputStream(localFile);        
    132             for (File cacheFile : cacheList) {                                // 遍历所有临时文件
    133                 FileInputStream in = new FileInputStream(cacheFile);        
    134                 byte[] buffer = new byte[8192];
    135                 int length;
    136                 while ((length = in.read(buffer)) != -1)    // 读取每个文件
    137                     out.write(buffer, 0, length);            // 把每个文件的数据都写出到localFile
    138                 in.close();
    139                 cacheFile.delete();
    140             }
    141             out.close();
    142         } catch (IOException e) {
    143             throw new RuntimeException(e);
    144         }
    145     }
    146     
    147     public void stop() {
    148         for (MyFileHandler fileHandler : handlerList) {
    149             fileHandler.cancel();
    150         }
    151     }
    152 }
    View Code
  • 相关阅读:
    重学计算机组成原理(七)- 程序无法同时在Linux和Windows下运行?
    学习ConcurrentHashMap并发写机制
    Unsafe中CAS的实现
    LinkedHashMap源码解读
    Hashset源码分析
    复盘MySQL分页查询优化方案
    Java面试必问之Hashmap底层实现原理(JDK1.8)
    Java面试必问之Hashmap底层实现原理(JDK1.7)
    Docker实战之Kafka集群
    Docker实战之Zookeeper集群
  • 原文地址:https://www.cnblogs.com/friends-wf/p/4528246.html
Copyright © 2020-2023  润新知