android多线程下载详解

TongsengsBlog 2012-05-10

本文将介绍在android平台下如何实现多线程下载,大家都知道,android平台使用java做为开发语言,所以java中支持的多线程下载方式在android平台下都支持,其中主要有两种方式可以实现多线程下载。

一种方式是使用很多个线程分别下载文件的不同部分,最后把所有下载完的文件合并成一个文件。另一种方式是使用java为我们提供的RandomAccessFile类实现多线程的下载。

从性能上分析,第二种方式的存取速度会慢一些,但开发起来较为容易,不需要进行合并文件等操作。本文将使用第二种方式来实现多线程下载,最终效果如下图所示:

android多线程下载详解

第一步,我们先写一个线程类,来完成对指定区域的数据进行下载,如下所示:

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) {
 
			}
 
		}
	}
}

相关推荐