Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android實現多線程斷點下載

Android實現多線程斷點下載

編輯:關於Android編程

本案例在於實現文件的多線程斷點下載,即文件在下載一部分中斷後,可繼續接著已有進度下載,並通過進度條顯示進度。也就是說在文件開始下載的同時,自動創建每個線程的下載進度的本地文件,下載中斷後,重新進入應用點擊下載,程序檢查有沒有本地文件的存在,若存在,獲取本地文件中的下載進度,繼續進行下載,當下載完成後,自動刪除本地文件。

1. 定義布局文件需要用到的屬性名及內容

\

2. 設置用戶的Internet權限和關於SD卡的權限



3. 開始界面的布局

基本效果圖如下:

\

用到兩個TextView控件,一個EditText控件,一個Button控件,一個ProgressBar控件<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+0OjSqtei0uK1xMrHo7q9+LbIzPXTwzxQcm9ncmVzc0JhciAvPr/YvP6jrMno1sNzeXRsZcr00NSjunN0eWxlPQ=="?android:attr/progressBarStyleHorizontal"

4.MainActivity的主要程序如下,代碼中有注釋詳解:

package www.csdn.net.download;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

import www.csdn.net.utils.StreamTools;
import android.R.integer;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class DownloadActivity extends Activity {

	// 線程開啟的數量
	private int threadNum = 3;
	private int threadRunning = 3;

	private EditText et_url;
	private ProgressBar progressBar;
	private TextView tv_pb;
	
	private int currentProgress;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_download);
		// 獲取控件對象
		et_url = (EditText) findViewById(R.id.et_url);
		progressBar = (ProgressBar) findViewById(R.id.pb_down);
		tv_pb = (TextView) findViewById(R.id.tv_pb);
		
		File sdDir = Environment.getExternalStorageDirectory();
		File pbFile = new File(sdDir,"pb.txt");
		InputStream is = null;
		try {
			//判斷文件是否存在
			if (pbFile.exists()) {
				is = new FileInputStream(pbFile);
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (is != null) {
			String value = StreamTools.streamToStr(is);
			String[] arr = value.split(";");
			progressBar.setMax(Integer.valueOf(arr[0]));//最大值
			currentProgress = Integer.valueOf(arr[1]);//當前值
			progressBar.setProgress(currentProgress);
			tv_pb.setText("當前的進度是:"+arr[2]);//顯示百分比
		}
	}

	// 下載文件(得到服務器端文件的大小)
	public void downLoadFile(View v) {

		// 獲取下載路徑
		final String spec = et_url.getText().toString();
		if (TextUtils.isEmpty(spec)) {
			Toast.makeText(this, "下載的地址不能為空", Toast.LENGTH_LONG).show();
		} else {
			new Thread() {
				public void run() {
					// HttpURLConnection
					try {
						// 根據下載的地址構建URL對象
						URL url = new URL(spec);
						// 通過URL對象的openConnection()方法打開連接,返回一個連接對象
						HttpURLConnection httpURLConnection = (HttpURLConnection) url
								.openConnection();
						// 設置請求的頭
						httpURLConnection.setRequestMethod("GET");
						httpURLConnection.setReadTimeout(5000);
						httpURLConnection.setConnectTimeout(5000);
						// 判斷是否響應成功
						if (httpURLConnection.getResponseCode() == 200) {
							// 獲取下載文件的長度
							int fileLength = httpURLConnection
									.getContentLength();
							//設置進度條的最大值
							progressBar.setMax(fileLength);
							//判斷sd卡是否管用
							if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
								// 保存文件
								// 外部存儲設備的路徑
								File sdFile = Environment
										.getExternalStorageDirectory();
								//獲取文件的名稱
								String fileName = spec.substring(spec.lastIndexOf("/")+1);
								//創建保存的文件
								File file = new File(sdFile, fileName);
								//創建可以隨機訪問對象
								RandomAccessFile accessFile = new RandomAccessFile(
										file, "rwd");
								// 保存文件的大小
								accessFile.setLength(fileLength);
								// 關閉
								accessFile.close();
								// 計算出每個線程的下載大小
								int threadSize = fileLength / threadNum;
								// 計算出每個線程的開始位置,結束位置
								for (int threadId = 1; threadId <= 3; threadId++) {
									int startIndex = (threadId - 1) * threadSize;
									int endIndex = threadId * threadSize - 1;
									if (threadId == threadNum) {// 最後一個線程
										endIndex = fileLength - 1;
									}

									System.out.println("當前線程:" + threadId
											+ " 開始位置:" + startIndex + " 結束位置:"
											+ endIndex + " 線程大小:" + threadSize);
									// 開啟線程下載
									new DownLoadThread(threadId, startIndex,
											endIndex, spec).start();
								}
							}else {
								DownloadActivity.this.runOnUiThread(new Runnable() {
									public void run() {
										Toast.makeText(DownloadActivity.this, "SD卡不管用", Toast.LENGTH_LONG).show();
									}
								});
							}
						}else {
							//在主線程中運行
							DownloadActivity.this.runOnUiThread(new Runnable() {
								public void run() {
									Toast.makeText(DownloadActivity.this, "服務器端返回錯誤", Toast.LENGTH_LONG).show();
								}
							});
						}

					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				};

			}.start();
		}
	}

	class DownLoadThread extends Thread {

		private int threadId;
		private int startIndex;
		private int endIndex;
		private String path;

		/**
		 * 構造函數
		 * 
		 * @param threadId
		 *            線程的序號
		 * @param startIndex
		 *            線程開始位置
		 * @param endIndex
		 * @param path
		 */
		public DownLoadThread(int threadId, int startIndex, int endIndex,
				String path) {
			super();
			this.threadId = threadId;
			this.startIndex = startIndex;
			this.endIndex = endIndex;
			this.path = path;
		}

		@Override
		public void run() {
			try {
				File sdFile = Environment.getExternalStorageDirectory();
				//獲取每個線程下載的記錄文件
				File recordFile = new File(sdFile, threadId + ".txt");
				if (recordFile.exists()) {
					// 讀取文件的內容
					InputStream is = new FileInputStream(recordFile);
					// 利用工具類轉換
					String value = StreamTools.streamToStr(is);
					// 獲取記錄的位置
					int recordIndex = Integer.parseInt(value);
					// 將記錄的位置賦給開始位置
					startIndex = recordIndex;
				}

				// 通過path路徑構建URL對象
				URL url = new URL(path);
				// 通過URL對象的openConnection()方法打開連接,返回一個連接對象
				HttpURLConnection httpURLConnection = (HttpURLConnection) url
						.openConnection();
				// 設置請求的頭
				httpURLConnection.setRequestMethod("GET");
				httpURLConnection.setReadTimeout(5000);
				// 設置下載文件的開始位置結束位置
				httpURLConnection.setRequestProperty("Range", "bytes="
						+ startIndex + "-" + endIndex);
				// 獲取的狀態碼
				int code = httpURLConnection.getResponseCode();
				// 判斷是否成功
				if (code == 206) {
					// 獲取每個線程返回的流對象
					InputStream is = httpURLConnection.getInputStream();
					//獲取文件的名稱
					String fileName = path.substring(path.lastIndexOf("/")+1);
					// 根據路徑創建文件
					File file = new File(sdFile, fileName);
					// 根據文件創建RandomAccessFile對象
					RandomAccessFile raf = new RandomAccessFile(file, "rwd");
					raf.seek(startIndex);
					// 定義讀取的長度
					int len = 0;
					// 定義緩沖區
					byte b[] = new byte[1024 * 1024];
					int total = 0;
					// 循環讀取
					while ((len = is.read(b)) != -1) {
						RandomAccessFile threadFile = new RandomAccessFile(
								new File(sdFile, threadId + ".txt"), "rwd");
						threadFile.writeBytes((startIndex + total) + "");
						threadFile.close();
						raf.write(b, 0, len);
						// 已經下載的大小
						total += len;
						//解決同步問題
						synchronized (DownloadActivity.this) {
							currentProgress += len;
							progressBar.setProgress(currentProgress);
							//計算百分比的操作 l表示long型
							final String percent = currentProgress*100l/progressBar.getMax()+"%";
							DownloadActivity.this.runOnUiThread(new Runnable() {
								public void run() {
									tv_pb.setText("當前的進度是:"+percent);
								}
							});
							//創建保存當前進度和百分比的操作
							RandomAccessFile pbFile = new RandomAccessFile(
									new File(sdFile, "pb.txt"), "rwd");
							pbFile.writeBytes(progressBar.getMax()+";"+currentProgress+";"+percent);
							pbFile.close();
						}
					}
					raf.close();
					is.close();
					runOnUiThread(new Runnable() {
						public void run() {
							Toast.makeText(DownloadActivity.this, "當前線程--" + threadId + "--下載完畢", Toast.LENGTH_LONG).show();
						}
					});
					deleteRecordFiles();
				} else {
					runOnUiThread(new Runnable() {
						public void run() {
							Toast.makeText(DownloadActivity.this, "服務器端下載錯誤", Toast.LENGTH_LONG).show();
						}
					});
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}

	}

	// synchronized避免線程同步
	public synchronized void deleteRecordFiles() {
		File sdFile = Environment.getExternalStorageDirectory();
		threadRunning--;
		if (threadRunning == 0) {
			for (int i = 1; i <= 3; i++) {
				File recordFile = new File(sdFile, i + ".txt");
				if (recordFile.exists()) {
					// 刪除文件
					recordFile.delete();
				}
				File pbFile = new File(sdFile,"pb.txt");
				if (pbFile.exists()) {
					pbFile.delete();
				}
			}
		}
	}
}

對於流的輸出可以封裝一個StreamTools方法,在主程序中可以應用,代碼如下:

package www.csdn.net.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class StreamTools {
	
	public static String streamToStr(InputStream is){
		String value = null;
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			// 定義讀取的長度
			int len = 0;
			// 定義緩沖區
			byte b[] = new byte[1024];
			// 循環讀取
			while ((len = is.read(b)) != -1) {
				baos.write(b, 0, len);
			}
			baos.close();
			is.close();
			value = new String(baos.toByteArray());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return value;
	}

}
5. 程序運行結果如圖:

\\

sd卡中出現的臨時文件,當下載完成會自動刪除:

\

6. 出現的bug原因可能有:

Internet權限沒加,服務器沒啟動,訪問下載路徑有錯,沒有獲取控件對象等。

如果文件下載中,進度條顯示的進度是負數,可能原因是文件大小進行百分比計算時超出內存空間,解決辦法:在定義百分比的時候,在100後面加上l,表示long型,即String percent = currentProgress*100l/progressBar.getMax()+"%"。



  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved