1. 程式人生 > >android多執行緒斷點下載

android多執行緒斷點下載

多執行緒斷電xia下載,通過設定執行緒的數量,動態獲取下載檔案執行緒的個數,這是本人用於練習所寫demo,註釋很詳細,用於初學者參考使用。

MainActivity.java頁面

 

package com.dahui.download;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;


import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ProgressBar;

public class MainActivity extends Activity {

	private EditText et_path;
	private EditText et_threadCount;
	private LinearLayout ll_pb_layout;
	private String path;
	private static int runningThread;  //代表當前正在執行的執行緒
	private int threadCount;
	private List<ProgressBar> pbList;//用來存進度條的引用

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		et_path = (EditText) findViewById(R.id.et_path);
		et_threadCount = (EditText) findViewById(R.id.et_threadCount);
		ll_pb_layout = (LinearLayout) findViewById(R.id.ll_pb);
		
		pbList = new ArrayList<ProgressBar>();
	}
	
	//點選下載按鈕
    public void click(View v){
    	path = et_path.getText().toString().trim();
    	//[3]獲取執行緒數量
    	String threadCountt = et_threadCount.getText().toString().trim();
    	//[3.1]先移除之前新增的進度條
    	ll_pb_layout.removeAllViews();
    	threadCount = Integer.parseInt(threadCountt);
    	
    	pbList.clear();//每次點選清空集合
    	
    	for (int i = 0; i < threadCount; i++) {
    		//[3.1]把定義的item佈局轉換成view物件
    		ProgressBar pbView = (ProgressBar) View.inflate(getApplicationContext(), R.layout.item, null);
    		
    		//[3.2]把pbView新增到集合中
    		pbList.add(pbView);
    		
			//[4]動態新增進度條
    		ll_pb_layout.addView(pbView);
		}
    	//[5]開始移植 聯網
    	new Thread(){public void run() {

    		//【1】★★★★★★獲取伺服器檔案大小  要計算每個執行緒開始位置和結束位置
    		try {
    			//String path = "http://192.168.231.2:8080/new1.xml";
    			URL url = new URL(path);
    			//[2.3]拿到HttpURLConnection物件    用於傳送或接受資料
    			HttpURLConnection coon = (HttpURLConnection) url.openConnection();
    			//[2.4]設定傳送get請求
    			coon.setRequestMethod("GET");//get大寫 預設就是get
    			//請求超時時間
    			coon.setConnectTimeout(5000);
    			//[2.6]獲取伺服器返回的狀態碼
    			int code = coon.getResponseCode();
    			//[2.7]如果code==200 說明請求成功
    			if (code == 200) {
    				int length = coon.getContentLength();
    				
    				runningThread = threadCount; //把執行緒的數量賦值給正在執行的執行緒
    				
    				System.out.println("length"+length);
    				//【2】★★★★★★建立一個大小和伺服器一模一樣的檔案  目的是把空間提前申請出來
    				RandomAccessFile rafAceAccessFile = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath()+"/"+getFilename(path), "rw");
    				rafAceAccessFile.setLength(length);
    				
    				//算出每個執行緒下載的大小
    				int blockSize = length / threadCount;
    				
    				//【3】★★★★★★計算每個執行緒開始位置和結束位置
    				for (int i = 0; i < threadCount; i++) {
    					int startIndex = i*blockSize; //執行緒開始位置
    					int endIndex = (i+1)*blockSize - 1;
    					if (i == threadCount -1) {
    						endIndex = length - 1;
    					}
    					
    					System.out.println("執行緒Id:"+i +"理論下載位置"+":"+startIndex+"-----"+endIndex);
    					
    					//【4】★★★★★★開啟執行緒去伺服器下載檔案
    					DownLoadThread downLoadThread = new DownLoadThread(startIndex, endIndex, i);
    					downLoadThread.start();
    				}
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	
    	};}.start();
    	
    }
    public String getFilename(String path){
		int start = path.lastIndexOf("/")+1;
		return path.substring(start);
	}
    
    private class DownLoadThread extends Thread{
		//通過構造方法把每個執行緒下載的開始位置和結束位置傳遞出來
		private int startIndex;
		private int endIndex;
		private int threadId;
		private int PbMaxSize; //代表當前執行緒下載的最大值
		//如果中斷過   則獲取上次下載位置
		private int pblastposition;
		
		public DownLoadThread(int startIndex,int endIndex,int threadId){
			this.startIndex = startIndex;
			this.endIndex = endIndex;
			this.threadId = threadId;
			
		}
		@Override
		public void run() {
			//實現去伺服器下載檔案的邏輯
			try {
				
				//[0]計算當前進度條的最大值
				PbMaxSize = endIndex-startIndex;
				URL url = new URL(path);
				//[2.3]拿到HttpURLConnection物件    用於傳送或接受資料
				HttpURLConnection coon = (HttpURLConnection) url
						.openConnection();
				//[2.4]設定傳送get請求
				coon.setRequestMethod("GET");//get大寫 預設就是get
				//請求超時時間
				coon.setConnectTimeout(5000);
				
				//★★★★★★★如果中間斷過,繼續上次的位置下載  從檔案中讀取上次下載的位置
				File file = new File(Environment.getExternalStorageDirectory().getPath()+"/"+getFilename(path)+threadId+".txt");
				if (file.exists()&&file.length()>0) {
					FileInputStream fis = new FileInputStream(file);
					BufferedReader buff = new BufferedReader(new InputStreamReader(fis));
					String lastPositionn = buff.readLine();//讀取出來的內容就是上一次下載的位置
					int lastPosition = Integer.parseInt(lastPositionn);
					
					//★★給我們定義的進度條賦值
					pblastposition = lastPosition - startIndex;
					
					//☆☆☆☆☆☆要改變一下startIndex位置
					startIndex = lastPosition;
					System.out.println("執行緒Id:"+threadId +"真實下載位置"+":"+startIndex+"-----"+endIndex);
					fis.close();
				}
				
				
				
				
				//設定請求頭Range (告訴伺服器每個執行緒的開始下載和結束位置)
				coon.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
				
				//[2.6]獲取伺服器返回的狀態碼
				int code = coon.getResponseCode();
				//[2.7]如果code==200 說明請求成功 206代表請求部分資源成功
				if (code == 206) {
					
					//建立隨機讀寫檔案物件
					RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath()+"/"+getFilename(path), "rw");
					//每個執行緒從自己的位置開始寫
					raf.seek(startIndex);
					
					//[2.8]獲取伺服器返回的資料   以流的形式返回(要把流轉換成字串,此操作比較常見,所以把它做成工具類)
					InputStream in = coon.getInputStream();//存的是UCBrowser.exe
					
					//9把資料寫到檔案中
					int len= -1;
					byte[] buffer = new byte[1024*1024];
					int total = 0;//代表當前執行緒下載的大小
					while ((len = in.read(buffer))!= -1) {
						raf.write(buffer,0,len);
					
						total+=len;
						//10.實現斷點續傳   記錄當前下載的位置
						int currentThreadPosition = startIndex+total;//把這個位置存起來
						//11.用來存當前執行緒下載的位置
						RandomAccessFile raff = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath()+"/"+getFilename(path)+threadId+".txt", "rwd");
						raff.write(String.valueOf(currentThreadPosition).getBytes());
						raff.close();
						
						//設定進度條的最大值和最小值
						pbList.get(threadId).setMax(PbMaxSize);//設定進度條最大值
						pbList.get(threadId).setProgress(pblastposition+total);//設定進度條當前進度
						
						
					}
					raf.close(); //關閉流 釋放資源
					System.out.println("執行緒id:"+threadId+"----下載完畢!");
					
					//下載完畢刪除斷點儲存的.txt檔案
					synchronized (this) {
						runningThread--;
						if(runningThread == 0){
							//說明所有執行緒執行完畢
							for (int i = 0; i < threadCount; i++) {
								File delteFile = new File(Environment.getExternalStorageDirectory().getPath()+"/"+getFilename(path)+i+".txt");
								delteFile.delete();
							}
						}
					}
					
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

}

 佈局頁面:activity_main.xml頁面

<LinearLayout 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"
    tools:context=".MainActivity" >

	<EditText 
	    android:id="@+id/et_path"
	    android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="http://192.168.231.2:8080/UCBrowser.exe"
        android:hint="請輸入下載的路徑"
	    />
	
	<EditText 
	    android:id="@+id/et_threadCount"
	    android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="請輸入執行緒的數量"
	    />
	
	<Button 
		android:onClick="click"
	    android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="下載"
	    />
	<LinearLayout 
	    android:id="@+id/ll_pb"
	    android:background="#080E12"
	    android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
	    >
	    
	    
	</LinearLayout>
</LinearLayout>

item.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="match_parent"
        android:layout_height="wrap_content" />


結果截圖:

資源原始碼包下載地址:https://download.csdn.net/download/qq_38993002/10759021