文件多线程下载实现

 

布局文件

 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

 

posted on 2015-05-25 16:39  wf110  阅读(1085)  评论(0编辑  收藏  举报