Android(java)学习笔记159:多线程断点下载的原理(Android实现)

之前在Android(java)学习笔记215中,我们从JavaSE的角度去实现了多线程断点下载,下面从Android角度实现这个断点下载

1. 新建一个Android工程:

(1)其中我们先实现布局文件activity_main.xml:

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical"
 6     tools:context=".MainActivity" >
 7 
 8     <EditText
 9         android:id="@+id/et_path"
10         android:layout_width="match_parent"
11         android:layout_height="wrap_content"
12         android:text="http://192.168.1.100:8080/" />
13 
14     <EditText
15         android:inputType="number"
16         android:id="@+id/et_count"
17         android:layout_width="match_parent"
18         android:layout_height="wrap_content"
19         android:hint="请设置下载线程的数量,默认3" />
20 
21     <Button
22         android:layout_width="match_parent"
23         android:layout_height="wrap_content"
24         android:onClick="download"
25         android:text="下载" />
26 
27     <LinearLayout
28         android:paddingLeft="5dip"
29         android:paddingRight="5dip"
30         android:id="@+id/ll_container"
31         android:layout_width="match_parent"
32         android:layout_height="match_parent"
33         android:orientation="vertical" >
34     </LinearLayout>
35 
36 </LinearLayout>

布局效果如下:

(2)其中的进度条样式pb.xml如下

<?xml version="1.0" encoding="utf-8"?>
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/progressBar1"
    style="?android:attr/progressBarStyleHorizontal"
    android:layout_width="fill_parent"
    android:layout_marginLeft="5dip"
    android:layout_marginRight="5dip"
    android:layout_height="wrap_content" />

2. MainActivity.java:

  1 package com.itheima.mutiledownloader;
  2 
  3 import java.io.BufferedReader;
  4 import java.io.File;
  5 import java.io.FileInputStream;
  6 import java.io.FileNotFoundException;
  7 import java.io.IOException;
  8 import java.io.InputStream;
  9 import java.io.InputStreamReader;
 10 import java.io.RandomAccessFile;
 11 import java.net.HttpURLConnection;
 12 import java.net.MalformedURLException;
 13 import java.net.ProtocolException;
 14 import java.net.URL;
 15 
 16 import android.app.Activity;
 17 import android.os.Bundle;
 18 import android.os.Environment;
 19 import android.text.TextUtils;
 20 import android.view.View;
 21 import android.widget.EditText;
 22 import android.widget.LinearLayout;
 23 import android.widget.ProgressBar;
 24 import android.widget.Toast;
 25 
 26 public class MainActivity extends Activity {
 27     private EditText et_path;
 28     private EditText et_count;
 29     private LinearLayout ll_container;
 30     private int threadCount = 3;
 31     private String path;
 32     protected int runningThreadCount;
 33     
 34     @Override
 35     protected void onCreate(Bundle savedInstanceState) {
 36         super.onCreate(savedInstanceState);
 37         setContentView(R.layout.activity_main);
 38         et_count = (EditText) findViewById(R.id.et_count);
 39         et_path = (EditText) findViewById(R.id.et_path);
 40         ll_container = (LinearLayout) findViewById(R.id.ll_container);
 41     }
 42 
 43     public void download(View view){
 44         String str_count = et_count.getText().toString().trim();
 45         path = et_path.getText().toString().trim();
 46         if(TextUtils.isEmpty(path)&&!path.startsWith("http://")){
 47             Toast.makeText(this, "下载路径不合法", 0).show();
 48             return;
 49         }
 50         if(!TextUtils.isEmpty(str_count)){
 51             threadCount = Integer.parseInt(str_count);
 52         }
 53         ll_container.removeAllViews();
 54         for(int i=0;i<threadCount;i++){
 55             ProgressBar pb = (ProgressBar) View.inflate(this, R.layout.pb,null);
 56             ll_container.addView(pb);
 57         }
 58         new Thread(){
 59             public void run() {
 60                 try {
 61                     URL url = new URL(path);
 62                     HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 63                     conn.setRequestMethod("GET");
 64                     int code = conn.getResponseCode();
 65                     if(code == 200){
 66                         int length = conn.getContentLength();
 67                         System.out.println("服务器文件的大小为:"+length);
 68                         //创建一个空白文件文件的大小和服务器资源一样
 69                         RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath()+"/"+getFileName(path), "rw");
 70                         raf.setLength(length);
 71                         raf.close();
 72                         //每个线程下载的平均区块大小
 73                         int blocksize = length / threadCount;
 74                         runningThreadCount = threadCount;
 75                         for(int threadId = 0;threadId<threadCount;threadId++){
 76                              int startIndex = threadId*blocksize;
 77                              int endIndex = (threadId+1)*blocksize-1;
 78                              if(threadId==(threadCount-1)){//最后一个线程的修正
 79                                  endIndex = length - 1;
 80                              }
 81                              new DownloadThread(startIndex, endIndex, threadId).start();
 82                         }
 83                     }
 84                 } catch (Exception e) {
 85                     e.printStackTrace();
 86                     showToast("下载失败");
 87                 }
 88                 
 89             };
 90         }.start();
 91         
 92     }
 93     class DownloadThread extends Thread{
 94         /**
 95          * 理论上第一次开始的位置
 96          */
 97         int firstStartIndex;
 98         int startIndex;
 99         int endIndex;
100         /**
101          * 当前线程下载到文件的位置
102          */
103         int filePosition;
104         int threadId;
105         /**
106          * 当前线程需要下载的总文件大小
107          */
108         int threadTotal;
109         ProgressBar pb;//当前线程对应的进度条
110         /**
111          * 
112          * @param startIndex 开始位置
113          * @param endIndex 结束位置
114          * @param threadId 线程id
115          */
116         public DownloadThread(int startIndex, int endIndex, int threadId) {
117             this.startIndex = startIndex;
118             this.firstStartIndex = startIndex;
119             this.endIndex = endIndex;
120             threadTotal = endIndex - startIndex;
121             this.threadId = threadId;
122             pb = (ProgressBar) ll_container.getChildAt(threadId);
123             pb.setMax(threadTotal);
124             filePosition = startIndex;
125         }
126 
127         @Override
128         public void run() {
129             //System.out.println("线程:"+threadId+"理论下载的位置:"+startIndex+"~~~"+endIndex);
130             //读取,看看有没有下载的历史数据,读下载的进度
131             try {
132             File file = new File(Environment.getExternalStorageDirectory().getPath()+"/"+threadId+getFileName(path)+".txt");//用一个文本记录当前线程下载的进程
133             if(file.exists()&&file.length()>0){
134                 FileInputStream fis = new FileInputStream(file);
135                 BufferedReader br = new BufferedReader(new InputStreamReader(fis));
136                 filePosition = Integer.parseInt(br.readLine());//上一次下载到文件的哪个位子。
137                 startIndex = filePosition;
138                 fis.close();
139             }
140             System.out.println("线程:"+threadId+"实际上下载的位置:"+startIndex+"~~~"+endIndex);
141                 URL url = new URL(path);
142                 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
143                 conn.setRequestMethod("GET");
144                 conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
145                 int code = conn.getResponseCode();//2XX 成功 3XX重定向 4XX资源找不到 5XX服务器异常
146                 if(code == 206){
147                     InputStream is = conn.getInputStream();
148                     RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath()+"/"+getFileName(path), "rwd");
149                     //一定要记得定位文件写的位置
150                     raf.seek(startIndex);
151                     byte[] buffer = new byte[1024*4];//缓冲区的大小
152                     int len = -1;
153                     while((len = is.read(buffer))!=-1){
154                         raf.write(buffer, 0, len);
155                         filePosition+=len;//记录写入数据的进度
156                         int process = filePosition - firstStartIndex;
157                         pb.setProgress(process);
158                         RandomAccessFile rafinfo = new RandomAccessFile(file, "rwd");
159                         rafinfo.write(String.valueOf(filePosition).getBytes());
160                         rafinfo.close();
161                     }
162                     raf.close();
163                     is.close();
164                     System.out.println("线程:"+threadId+"下载完毕了。");
165                     synchronized (MainActivity.this) {//加锁,同步运行,保证下面一段代码在同一个时间片运行,原子性执行
166                         runningThreadCount--;
167                         if(runningThreadCount==0){
168                             System.out.println("所有的线程都下载完毕了");
169                             showToast("下载完毕了");
170                             for(int i =0;i<threadCount;i++){
171                                 File f = new File(Environment.getExternalStorageDirectory().getPath()+"/"+i+getFileName(path)+".txt");
172                                 System.out.println(f.delete());
173                             }
174                         }
175                     }
176                     
177                 }
178             } catch (Exception e) {
179                 e.printStackTrace();
180             }
181         }
182     }
183     /**
184      * 获取路径对应的文件名
185      * @param path
186      * @return
187      */
188     private static String getFileName(String path){
189         int beginIndex = path.lastIndexOf("/")+1;
190         return path.substring(beginIndex);
191     }
192     
193     
194     private void showToast(final String text){
195         runOnUiThread(new Runnable() {
196             @Override
197             public void run() {
198                 Toast.makeText(MainActivity.this, text, 0).show();
199             }
200         });
201     }
202 }

3. 其中AndroidMainfest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.itheima.mutiledownloader"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.itheima.mutiledownloader.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

 

posted on 2015-09-08 21:58  鸿钧老祖  阅读(243)  评论(0)    收藏  举报

导航