多線程下載

本文將介紹在android平臺下如何實現多線程下載,大家都知道,android平臺使用java做爲開發語言,所以java中支持的多線程下載方式在android平臺下都支持,其中主要有兩種方式可以實現多線程下載。

一種方式是使用很多個線程分別下載文件的不同部分,最後把所有下載完的文件合併成一個文件。另一種方式是使用java爲我們提供的RandomAccessFile類實現多線程的下載。

從性能上分析,第二種方式的存取速度會慢一些,但開發起來較爲容易,不需要進行合併文件等操作。本文將使用第二種方式來實現多線程下載,最終效果如下圖所示:


第一步,我們先寫一個線程類,來完成對指定區域的數據進行下載,如下所示:

package com.ideasandroid.demo;
 
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URL;
import java.net.URLConnection;
 
import android.util.Log;
/**
 *  Copyright (C) 2010 ideasandroid
 *  演示android多線程下載
 *  歡迎訪問http://www.ideasandroid.com
 *  讓程序開發不再那麼神祕
 *  
 *  單個下載線程
 */
public class FileDownloadThread extends Thread{
	private static final int BUFFER_SIZE=1024;
	private URL url;
	private File file;
	private int startPosition;
	private int endPosition;
	private int curPosition;
	//用於標識當前線程是否下載完成
	private boolean finished=false;
	private int downloadSize=0;
	public FileDownloadThread(URL url,File file,int startPosition,int endPosition){
		this.url=url;
		this.file=file;
		this.startPosition=startPosition;
		this.curPosition=startPosition;
		this.endPosition=endPosition;
	}
	@Override
	public void run() {
        BufferedInputStream bis = null;
        RandomAccessFile fos = null;                                               
        byte[] buf = new byte[BUFFER_SIZE];
        URLConnection con = null;
        try {
            con = url.openConnection();
            con.setAllowUserInteraction(true);
            //設置當前線程下載的起點,終點
            con.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);
            //使用java中的RandomAccessFile 對文件進行隨機讀寫操作
            fos = new RandomAccessFile(file, "rw");
            //設置開始寫文件的位置
            fos.seek(startPosition);
            bis = new BufferedInputStream(con.getInputStream());  
            //開始循環以流的形式讀寫文件
            while (curPosition < endPosition) {
                int len = bis.read(buf, 0, BUFFER_SIZE);                
                if (len == -1) {
                    break;
                }
                fos.write(buf, 0, len);
                curPosition = curPosition + len;
                if (curPosition > endPosition) {
                	downloadSize+=len - (curPosition - endPosition) + 1;
                } else {
                	downloadSize+=len;
                }
            }
            //下載完成設爲true
            this.finished = true;
            bis.close();
            fos.close();
        } catch (IOException e) {
          Log.d(getName() +" Error:", e.getMessage());
        }
	}
 
	public boolean isFinished(){
		return finished;
	}
 
	public int getDownloadSize() {
		return downloadSize;
	}
}

接下來就是使用圖形界面來獲取需要下載的內容,並實時更新下載進度條,代碼如下所示:

package com.ideasandroid.demo;
 
import java.io.File;
import java.net.URL;
import java.net.URLConnection;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
/**
 *  Copyright (C) 2010 ideasandroid
 *  演示android多線程下載
 *  歡迎訪問http://www.ideasandroid.com
 *  讓程序開發不再那麼神祕
 */
public class FileDownloadDemo extends Activity {
 
	private EditText downloadUrl;
	private EditText downloadFileName;
	private EditText downloadThreadNum;
	private Button downloadBt;
	private ProgressBar downloadProgressBar;
	private TextView progressMessage;
	private int downloadedSize = 0;
	private int fileSize = 0;
 
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
 
		downloadUrl = (EditText) findViewById(R.id.downloadUrl);
		downloadFileName = (EditText) findViewById(R.id.downloadFileName);
		downloadThreadNum = (EditText) findViewById(R.id.downloadThreadNum);
		progressMessage = (TextView) findViewById(R.id.progressMessage);
		downloadBt = (Button) findViewById(R.id.downloadBt);
		downloadProgressBar = (ProgressBar) findViewById(R.id.downloadProgressBar);
		downloadProgressBar.setVisibility(View.VISIBLE);
		downloadProgressBar.setMax(100);
		downloadProgressBar.setProgress(0);
		downloadBt.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				download();
			}
		});
	}
 
	private void download() {
		// 獲取SD卡目錄
		String dowloadDir = Environment.getExternalStorageDirectory()
				+ "/ideasdownload/";
		File file = new File(dowloadDir);
		//創建下載目錄
		if (!file.exists()) {
			file.mkdirs();
		}
 
		//讀取下載線程數,如果爲空,則單線程下載
		int downloadTN = Integer.valueOf("".equals(downloadThreadNum.getText()
				.toString()) ? "1" : downloadThreadNum.getText().toString());
		//如果下載文件名爲空則獲取Url尾爲文件名
		int fileNameStart = downloadUrl.getText().toString().lastIndexOf("/");
		String fileName = "".equals(downloadFileName.getText().toString()) ? downloadUrl
				.getText().toString().substring(fileNameStart)
				: downloadFileName.getText().toString();
		//開始下載前把下載按鈕設置爲不可用
		downloadBt.setClickable(false);
		//進度條設爲0
		downloadProgressBar.setProgress(0);
		//啓動文件下載線程
		new downloadTask(downloadUrl.getText().toString(), Integer
				.valueOf(downloadTN), dowloadDir + fileName).start();
	}
 
	Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			//當收到更新視圖消息時,計算已完成下載百分比,同時更新進度條信息
			int progress = (Double.valueOf((downloadedSize * 1.0 / fileSize * 100))).intValue();
			if (progress == 100) {
				downloadBt.setClickable(true);
				progressMessage.setText("下載完成!");
			} else {
				progressMessage.setText("當前進度:" + progress + "%");
			}
			downloadProgressBar.setProgress(progress);
		}
 
	};
 
	/**
	 * @author ideasandroid
	 * 主下載線程
	 */
	public class downloadTask extends Thread {
		private int blockSize, downloadSizeMore;
		private int threadNum = 5;
		String urlStr, threadNo, fileName;
 
		public downloadTask(String urlStr, int threadNum, String fileName) {
			this.urlStr = urlStr;
			this.threadNum = threadNum;
			this.fileName = fileName;
		}
 
		@Override
		public void run() {
			FileDownloadThread[] fds = new FileDownloadThread[threadNum];
			try {
				URL url = new URL(urlStr);
				URLConnection conn = url.openConnection();
				//獲取下載文件的總大小
				fileSize = conn.getContentLength();
				//計算每個線程要下載的數據量
				blockSize = fileSize / threadNum;
				// 解決整除後百分比計算誤差
				downloadSizeMore = (fileSize % threadNum);
				File file = new File(fileName);
				for (int i = 0; i < threadNum; i++) {
					//啓動線程,分別下載自己需要下載的部分
					FileDownloadThread fdt = new FileDownloadThread(url, file,
							i * blockSize, (i + 1) * blockSize - 1);
					fdt.setName("Thread" + i);
					fdt.start();
					fds[i] = fdt;
				}
				boolean finished = false;
				while (!finished) {
					// 先把整除的餘數搞定
					downloadedSize = downloadSizeMore;
					finished = true;
					for (int i = 0; i < fds.length; i++) {
						downloadedSize += fds[i].getDownloadSize();
						if (!fds[i].isFinished()) {
							finished = false;
						}
					}
					//通知handler去更新視圖組件
					handler.sendEmptyMessage(0);
					//休息1秒後再讀取下載進度
					sleep(1000);
				}
			} catch (Exception e) {
 
			}
 
		}
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章