[轉載:]
http://www.ideasandroid.com/?p=328
本文將介紹在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) {
}
}
}
}
程序中做了詳細的注釋,這里不再闡述。
有問題請留言!
posted on 2010-08-17 17:16
MEYE 閱讀(2367)
評論(0) 編輯 收藏