android實現多線程下載

實現多線程下載的大概思路:(假設文件大小是6M,共有三條線程同時執行)

●首先要根據訪問的URL路徑去調用openConnection方法,得到HttpUrlConnection對象,HttpUrlConnection對象調用它的方法得到下載文件的長度,然後設置本地文件的長度。

點擊(此處)摺疊或打開

  1. int length = HttpURLConnection.getContentLength();
  2. RandomAccessFile file = new RandomAccessFile("calendar_setup.exe","rw");
  3. file.setLength(length);
        RandomAccessFile和File的區別:
             RandomAccessFile同時將FileInputStream和FileOutputStream整合到一起,而且支持將從文件任意字節處都或寫數據;File類只是將文件作爲整體來處理文件,不能讀寫文件

  ●根據文件長度和線程數計算每條線程下載的數據長度和下載位置,如文件長度爲6M,線程數爲3,那麼每條線程下載數據的長度爲2M,

                                                                 
       問題:在下載時如何指定這些線程開始下載的位置?→HTTP協議

  ●使用HTTP的Range頭字段指定每條線程從文件的什麼位置開始下載,如指定從文件的2M位置開始下載:

點擊(此處)摺疊或打開

  1. HttpURLConnection.setRequestProperty("Range","bytes=2097152-");
        設置請求頭Range字段,就是bytes=2097152,2MB的字節,比如上圖的線程2,在下載的過程中只要設置了這個頭,那麼它就會從線程1和線程3開始下載

  ●保存文件,使用RandomAccessFile類指定每條線程從本地的什麼位置開始寫入數據

 

點擊(此處)摺疊或打開

  1. RandomAccessFile file = new RandomAccessFile("calendar_setup.exe","rw");
  2. file.seek(2097152);

小案例:

 ●編寫佈局文件,主要有一個進度條,一個Button,一個EditText


點擊(此處)摺疊或打開

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:orientation="vertical"
  4.     android:layout_width="fill_parent"
  5.     android:layout_height="fill_parent"
  6.     >
  7. <TextView 
  8.     android:layout_width="fill_parent" 
  9.     android:layout_height="wrap_content" 
  10.     android:text="下載"
  11.     />
  12. <EditText
  13.     android:layout_width="fill_parent"
  14.     android:layout_height="wrap_content"
  15.     android:hint="請輸入下載路徑"
  16.     android:text="http://file1.updrv.com/soft/rili/calendar_setup.exe"
  17.     android:id = "@+id/et_path"/>
  18. <ProgressBar
  19.     android:id="@+id/bar"
  20.     style="?android:attr/progressBarStyleHorizontal"
  21.     android:layout_width="fill_parent"
  22.     android:layout_height="wrap_content"/>
  23.  <TextView
  24.     android:id="@+id/tv_process"
  25.     android:layout_width="fill_parent"
  26.     android:layout_height="wrap_content"/>
  27.  <Button
  28.     android:layout_width="fill_parent"
  29.     android:layout_height="wrap_content"
  30.     android:text="開始下載"
  31.     android:id="@+id/bt"/>
  32. </LinearLayout>


●編寫activity文件,


點擊(此處)摺疊或打開

  1. public class DownLoadActivity extends Activity implements OnClickListener {
  2.     //定義相關控件
  3.     ProgressBar bar;
  4.     Button bt;
  5.     TextView tv ;
  6.     EditText et_text;
  7.     boolean flag = true;
  8.     boolean stopflag = false;
  9.     Handler handler = new Handler(){

  10.         @Override
  11.         public void handleMessage(Message msg) {
  12.             bar.setProgress(total);
  13.             int max = bar.getMax();
  14.             if (total>=(max-1)) {
  15.                 total = max;
  16.                 flag = false;
  17.             }
  18.             int result = total*100/max;
  19.             tv.setText("當前進度:"+result+"%");
  20.             super.handleMessage(msg);
  21.         }
  22.         
  23.     };
  24.     int total = 0;
  25.     @Override
  26.     public void onCreate(Bundle savedInstanceState) {
  27.         super.onCreate(savedInstanceState);
  28.         setContentView(R.layout.main);
  29.         //初始化相關控件
  30.         bar = (ProgressBar) this.findViewById(R.id.bar);
  31.         bt = (Button)this.findViewById(R.id.bt);
  32.         tv = (TextView)this.findViewById(R.id.tv_process);
  33.         et_text=(EditText)this.findViewById(R.id.et_path);
  34.         bt.setOnClickListener(this);
  35.     }
  36.     @Override
  37.     public void onClick(View v) {
  38.         switch (v.getId()) {
  39.         case R.id.bt:
  40.             if ("開始下載".equals(bt.getText().toString().trim())) {
  41.                 bt.setText("暫停");
  42.                 stopflag = false;
  43.             }else {
  44.                 bt.setText("開始下載");
  45.                 stopflag=true;
  46.             }
  47.             new Thread(){

  48.                 @Override
  49.                 public void run() {
  50.                     while (flag) {
  51.                         try {
  52.                             sleep(1000);
  53.                             Message message = new Message();
  54.                             //通知更新UI
  55.                             handler.sendMessage(message);
  56.                         } catch (Exception e) {
  57.                             // TODO: handle exception
  58.                         }
  59.                     }
  60.                     super.run();
  61.                 }
  62.                 
  63.             }.start();
  64.             //得到下載路徑
  65.             String path = et_text.getText().toString().trim();
  66.             if ("".equals(path)) {
  67.                 Toast.makeText(this, "路徑不能爲空", 0).show();
  68.                 return;
  69.             }
  70.             try {
  71.                 URL url = new URL(path);
  72.                 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  73.                 //設置請求的相關參數
  74.                 conn.setRequestMethod("GET");
  75.                 conn.setConnectTimeout(5000);
  76.                 conn.setRequestProperty("User-Agent",
  77.                         "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
  78.                 int code = conn.getResponseCode();
  79.                 if (code ==200) {
  80.                     int length = conn.getContentLength();
  81.                     RandomAccessFile file = new RandomAccessFile("/mnt/sdcard/" + getFileName(path), "rwd");
  82.                  //設置文件長度
  83.                     file.setLength(length);
  84.                     //設置進度條最大值
  85.                  bar.setMax(length);
  86.                  //建立3條線程下載數據
  87.                  int threadNO = 3;
  88.                  //每條線程下載數據的大小
  89.                  int blocksize = length/threadNO;
  90.                  
  91.                  for (int i = 0; i <threadNO; i++) {
  92.                         int startposition = i*blocksize;//每條線程下載的開始位置
  93.                         int endposition = (i+1)*blocksize;//每條線程下載的結束位置
  94.                         if (i==(threadNO-1)) {
  95.                             endposition = length;//如果整個文件的大小不爲線程個數的整數倍,則最後一個線程的結束位置即爲文件的總長度
  96.                         }
  97.                         DownLoadTask task = new DownLoadTask(i,path,startposition,endposition);
  98.                         task.start();
  99.                     }
  100.                 }
  101.             } catch (Exception e) {
  102.                 // TODO: handle exception
  103.                 Toast.makeText(this,"下載出現異常",0).show();
  104.             }
  105.             
  106.             break;
  107.         }
  108.     }
  109.     class DownLoadTask extends Thread{
  110.         int threadid;
  111.         String path;
  112.         int startposition;
  113.         int endposition;
  114.         public DownLoadTask(int threadid, String path, int startposition,
  115.                 int endposition) {
  116.             this.threadid = threadid;
  117.             this.path = path;
  118.             this.startposition = startposition;
  119.             this.endposition = endposition;
  120.         }
  121.         @Override
  122.         public void run() {
  123.             // TODO Auto-generated method stub
  124.             try {
  125.                 //爲每個線程創建一個文件用於記錄暫停下載時當時已下載的數據大小
  126.                 File postionfile = new File("/mnt/sdcard/" + threadid + ".txt");
  127.                 URL url = new URL(path);
  128.                 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  129.                 System.out.println("線程" + threadid + "正在下載 " + "開始位置 : "
  130.                         + startposition + "結束位置 " + endposition);
  131.                 if (postionfile.exists()) {
  132.                     FileInputStream is = new FileInputStream(postionfile);
  133.                     //處理流信息得到以下載的字節數大小
  134.                     byte[] result = StreamTool.getByte(is); 
  135.                     String str = new String(result);
  136.                     if (!"".equals(str)) {
  137.                         //如果postionfile中有記錄,則改變開始下載的位置
  138.                         int newstartposition = Integer.parseInt(str);
  139.                         if (newstartposition>startposition) {
  140.                             startposition = newstartposition;
  141.                         }
  142.                     }
  143.                 }
  144.                 
  145.                 conn.setRequestProperty("Range", "bytes=" + startposition + "-"
  146.                         + endposition);
  147.                 conn.setRequestMethod("GET");
  148.                 conn.setConnectTimeout(5000);
  149.                 conn.setRequestProperty("User-Agent",
  150.                         "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
  151.                 InputStream is = conn.getInputStream();
  152.                 RandomAccessFile file = new RandomAccessFile("/mnt/sdcard/"
  153.                         + getFileName(path), "rwd");
  154.                 file.seek(startposition);
  155.                 byte[] buffer = new byte[1024];
  156.                 int len = 0;
  157.                 int currentPosition = startposition;
  158.                 while ((len = is.read(buffer))!=-1) {
  159.                     if (stopflag) {
  160.                         return;
  161.                     }
  162.                     file.write(buffer, 0, len);
  163.                     synchronized (DownLoadActivity.this) {
  164.                         total+=len;
  165.                     }
  166.                     //記錄當前線程以下載的數據
  167.                     currentPosition+=len;
  168.                     String position = currentPosition+"";
  169.                     FileOutputStream out = new FileOutputStream(postionfile);
  170.                     //把記錄寫入文件
  171.                     out.write(position.getBytes());
  172.                     out.flush();
  173.                     out.close();
  174.                     
  175.                 }
  176.                 file.close();
  177.                 System.out.println("線程"+threadid+"下載完畢");
  178.                 if (postionfile.exists()) {
  179.                     postionfile.delete();
  180.                 }
  181.             } catch (Exception e) {
  182.             }
  183.             super.run();
  184.         }
  185.     }
  186.     public String getFileName(String path){
  187.         int start = path.lastIndexOf("/")+1;
  188.         return path.substring(start, path.length());
  189.     }
  190. }

  ●最後一個就是一個處理輸入流的工具類,返回字節數:


點擊(此處)摺疊或打開

  1. public class StreamTool {
  2.     public static byte[] getByte(InputStream is) throws Exception{
  3.         ByteArrayOutputStream out = new ByteArrayOutputStream();
  4.         int len = 0;
  5.         byte[] b = new byte[1024];
  6.         while ((len = is.read(b))!=-1) {
  7.             out.write(b, 0, len);
  8.         }
  9.         is.close();
  10.         out.flush();
  11.         return out.toByteArray();
  12.     }
  13. }

運行後看到^0^


暫停後:

發佈了7 篇原創文章 · 獲贊 14 · 訪問量 32萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章