Android 支持断点续传的多线程批量下载

1.断点续传功能需要服务器支持

2.开启线程池进行任务的加载

3.支持进度的回调

4.支持终止正在进行和未进行的下载任务

5.调用简单

6.对服务端是否支持断点续传的判断机制不够完善,使用者需自己确定是否支持断点续传


单个任务下载DownloadTask

多个任务下载DownloadTaskPool


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:onClick="onClickStart"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="开始" />
    
    <Button
        android:onClick="onClickRestart"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="继续" />

    <Button
        android:onClick="onClickStop"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="停止" />

    <ProgressBar
        android:id="@+id/progressBar1"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <ProgressBar
        android:id="@+id/progressBar2"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <ProgressBar
        android:id="@+id/progressBar3"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <ProgressBar
        android:id="@+id/progressBar4"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>



public class BaseActivity extends Activity {
	private DownloadTask task1;
	private ProgressBar progressBar1;
	private ProgressBar progressBar2;
	private ProgressBar progressBar3;
	private ProgressBar progressBar4;
	private DownloadTaskPool taskPool;
	private DownloadTask task2;
	private DownloadTask task3;
	private DownloadTask task4;
	private HashMap<Object, ProgressBar> barMap = new HashMap<Object, ProgressBar>();
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_base);
		progressBar1 = (ProgressBar) findViewById(R.id.progressBar1);
		progressBar1.setMax(100);
		progressBar2 = (ProgressBar) findViewById(R.id.progressBar2);
		progressBar2.setMax(100);
		progressBar3 = (ProgressBar) findViewById(R.id.progressBar3);
		progressBar3.setMax(100);
		progressBar4 = (ProgressBar) findViewById(R.id.progressBar4);
		progressBar4.setMax(100);

		task1 = new DownloadTask(
				"http://www.windows7en.com/uploads/140829/2010060910062251.jpg",
				Environment.getExternalStorageDirectory() + "/test01.jpg", true);
		
		task2 = new DownloadTask(
				"http://pubcn.net/Photo/UploadPhotos/200711/20071109020411176.jpg",
				Environment.getExternalStorageDirectory() + "/test02.jpg", true);
		task3 = new DownloadTask(
				"http://pubcn.net/Photo/UploadPhotos/200711/20071106122927651.jpg",
				Environment.getExternalStorageDirectory() + "/test03.jpg", true);
		task4 = new DownloadTask(
				"http://pubcn.net/Photo/UploadPhotos/200711/20071109013917774.jpg",
				Environment.getExternalStorageDirectory() + "/test04.jpg", true);
		
		barMap.put(task1, progressBar1);
		barMap.put(task2, progressBar2);
		barMap.put(task3, progressBar3);
		barMap.put(task4, progressBar4);
		
		taskPool = new DownloadTaskPool() {

			@Override
			public void onTaskStateChanged(TaskState state, DownloadTask task) {
				switch (state) {
				case START:
					Toast.makeText(BaseActivity.this, "开始下载",
							Toast.LENGTH_SHORT).show();
					break;
				case STOP:
					Toast.makeText(BaseActivity.this, "终止下载",
							Toast.LENGTH_SHORT).show();
					break;
				case FAIL:
					Toast.makeText(BaseActivity.this, "下载失败",
							Toast.LENGTH_SHORT).show();
					break;
				case SUCCEED:
					Toast.makeText(BaseActivity.this, "下载完成",
							Toast.LENGTH_SHORT).show();
					break;
				}
			}

			@Override
			public void onProgressIncreased(long current, long total, DownloadTask task) {
				ProgressBar pb = barMap.get(task);
				if(0!=total){
					pb.setProgress((int) (current*100/total));
				}
				
			}
		};
	}

	public void onClickStart(View v) {
		taskPool.addTask(task1);
		taskPool.addTask(task2);
		taskPool.addTask(task3);
		taskPool.addTask(task4);
		
	}

	public void onClickStop(View v) {
		taskPool.removeTask(task1);
		taskPool.removeTask(task2);
		taskPool.removeTask(task3);
		taskPool.removeTask(task4);
	}

	public void onClickRestart(View v) {
		taskPool.addTask(task1);
		taskPool.addTask(task2);
		taskPool.addTask(task3);
		taskPool.addTask(task4);
	}

}



public enum TaskState {
	SUCCEED,
	FAIL,
	STOP,
	START
}



public class DownloadTask implements Runnable{
	
	private static final String TMP_SUFFIX = ".tmp";
	/**
	 * 存放下载后的文件路径
	 */
	private String filePath;
	/**
	 * 
	 */
	private String tmpFilePath;
	/**
	 * 图片网址
	 */
	private String url;
	
	private long transferedBytes;
	
	private long totalBytes;
	
	private boolean isCanceled;
	
	private Thread thread;
	
	private DownloadTaskCallBack callBack;
	
	/**
	 * 是否支持断点续传
	 */
	private boolean isBrokenPointResumeAllowed;
	
	/**
	 * 
	 * @param url 要下载的文件的URL地址
	 * @param filePath 下载后的文件的存储路径
	 * @param allowBrokenPointResume true 支持断点续传,要求服务器支持才行,否则下载的文件有问题。 false 不支持断点续传
	 */
	public DownloadTask(String url, String filePath, boolean allowBrokenPointResume){
		this.url = url;
		this.filePath = filePath;
		this.tmpFilePath = filePath+TMP_SUFFIX;
		this.isBrokenPointResumeAllowed = allowBrokenPointResume;
	}
	
	
	
	public void run() {
		if(isCanceled){
			if(null!=callBack){
				callBack.onTaskStateChanged(TaskState.START, this);
				callBack.onProgressIncreased(100, 100, this);
				callBack.onTaskStateChanged(TaskState.SUCCEED, this);
			}
			return;
		}
		if(null!=callBack){
			callBack.onTaskStateChanged(TaskState.START, this);
		}
		boolean result = false;
		try {
			FileOutputStream fos = null;
			HttpClient client = new DefaultHttpClient();
			HttpGet getRequest = new HttpGet(url);
			File tmpFile = new File(tmpFilePath);
			if(isBrokenPointResumeAllowed && tmpFile.exists()){
				FileInputStream fis = new FileInputStream(tmpFile);
				transferedBytes = fis.available();
				fis.close();
				//请求断点续传
				Header header = new BasicHeader("Range", "bytes=" + transferedBytes+'-');  
				getRequest.addHeader(header);  
			}
			fos = new FileOutputStream(tmpFile, isBrokenPointResumeAllowed);
			HttpEntity entity = client.execute(getRequest).getEntity();
			Log.e("", entity.toString());
			totalBytes = entity.getContentLength();
			BufferedInputStream bis = new BufferedInputStream(entity.getContent(), 1024);
			byte[] buffer = new byte[1024];
			int n = 0;
			do{
				if(isCanceled){
					fos.close();
					getRequest.abort();
					callBack.onTaskStateChanged(TaskState.STOP, this);
					thread = null;
					return;
				}
				fos.write(buffer, 0, n);
				transferedBytes += n;
				if(null!=callBack){
					callBack.onProgressIncreased(transferedBytes, totalBytes, this);
				}
				if(transferedBytes>=totalBytes){
					break;
				}
				n = bis.read(buffer);
			}while(n!=-1);
			fos.close();
			result = tmpFile.renameTo(new File(filePath));
			getRequest.abort();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		callBack.onTaskStateChanged(result ? TaskState.SUCCEED : TaskState.FAIL , this);
		thread = null;
	}



	/**
	 * 单独使用时设置下载的回调
	 * @param callBack
	 */
	public void setCallBack(DownloadTaskCallBack callBack) {
		this.callBack = callBack;
	}


	public boolean fileExists(){
		return new File(filePath).exists();
	}
	
	public void start() {
		isCanceled = false;
		if(null==thread){
			transferedBytes = 0;
			thread = new Thread(this);
			thread.start();
		}
	}


	
	public void abort() {
		isCanceled = true;
	}

	void setActive(){
		isCanceled = false;
	}
	
	DownloadTaskPool getBindedTaskPool(){
		return bindedTaskPool;
	}
	
	void setBindedTaskPool(DownloadTaskPool taskPool){
		this.bindedTaskPool = taskPool;
	}
	
	private DownloadTaskPool bindedTaskPool;
	
	public long getTransferedBytes() {
		return transferedBytes;
	}


	public long getTotalBytes() {
		return totalBytes;
	}



	public String getUrl(){
		return url;
	}
	public String getFilePath(){
		return filePath;
	}
	
}



public abstract class DownloadTaskPool {
	final static int MSG_PROGRESS = 0;
	final static int MSG_START = 1;
	final static int MSG_STOP = 2;
	final static int MSG_FAIL = 3;
	final static int MSG_SUCCEED = 4;
	/**
	 * 返回1解码成功,返回2下载成功,返回3解码失败,返回4下载失败
	 */
	private static Handler handler = new Handler() {
		public void handleMessage(Message msg) {
			DownloadTask task = (DownloadTask) msg.obj;
			switch(msg.what){
			case MSG_PROGRESS:
				task.getBindedTaskPool().onProgressIncreased(task.getTransferedBytes(), task.getTotalBytes(), task);
				break;
			case MSG_START:
				task.getBindedTaskPool().onTaskStateChanged(TaskState.START, task);
				break;
			case MSG_STOP:
				task.getBindedTaskPool().onTaskStateChanged(TaskState.STOP, task);
				break;
			case MSG_FAIL:
				task.getBindedTaskPool().onTaskStateChanged(TaskState.FAIL, task);
				break;
			case MSG_SUCCEED:
				task.getBindedTaskPool().onTaskStateChanged(TaskState.SUCCEED, task);
				break;
			}
		};
	};
	
	
	
	private DownloadTaskCallBack callBack = new DownloadTaskCallBack() {
		
		@Override
		public void onProgressIncreased(long current, long total, DownloadTask task) {
			handler.removeMessages(MSG_PROGRESS, task);
			Message msg = Message.obtain(handler);
			msg.obj = task;
			msg.sendToTarget();
		}
		@Override
		public void onTaskStateChanged(TaskState state, DownloadTask task) {
			Message msg = Message.obtain(handler);
			msg.obj = task;
			switch(state){
			case START:
				msg.what = MSG_START;
				break;
			case STOP:
				msg.what = MSG_STOP;
				break;
			case FAIL:
				msg.what = MSG_FAIL;
				break;
			case SUCCEED:
				msg.what = MSG_SUCCEED;
				break;
			}
			msg.sendToTarget();
		}
	};
	
	
	
	private ExecutorService taskExecutor = Executors.newFixedThreadPool(3);
	private HashSet<DownloadTask> taskSet = new HashSet<DownloadTask>();
	public void addTask(DownloadTask task){
		if(!taskSet.contains(task)){
			if(task.fileExists()){
				return;
			}else{
				task.setCallBack(callBack);
				taskSet.add(task);
				task.setBindedTaskPool(this);
				task.setActive();
				taskExecutor.execute(task);
			}
		}
	}
	
	public abstract void onProgressIncreased(long current, long total, DownloadTask task);
	public abstract void onTaskStateChanged(TaskState state, DownloadTask task);
	
	public void removeTask(DownloadTask task){
		task.abort();
		taskSet.remove(task);
	}
	
	public void shutDown(){
		taskExecutor.shutdown();
		for(DownloadTask task : taskSet){
			task.abort();
		}
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值