android中多线程下载

package cs.service;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import android.os.Handler;
import android.os.Message;

public class BreakpointDownloader {
	private static final String DIR_PATH = "/mnt/sdcard/";	// 下载目录
	private static final int THREAD_AMOUNT = 3;				// 总线程数
	
	private URL url;			// 目标下载地址
	private File dataFile;		// 本地文件
	private File tempFile;		// 用来存储每个线程下载的进度的临时文件
	private long threadLen;		// 每个线程要下载的长度
	private long totalFinish;	// 总共完成了多少
	private long totalLen;		// 服务端文件总长度
	private long begin;			// 用来记录开始下载时的时间

	private Handler handler;
	private boolean isPause;


	public BreakpointDownloader(String address, Handler handler) throws Exception {
		url = new URL(address);															// 记住下载地址
		dataFile = new File(DIR_PATH, address.substring(address.lastIndexOf("/") + 1));	// 截取地址中的文件名, 创建本地文件
		tempFile = new File(dataFile.getAbsolutePath() + ".temp");						// 在本地文件所在文件夹中创建临时文件
		this.handler = handler;

	}
	
	public void pause() {
		isPause = true;
	}

	public void download() throws IOException {
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setConnectTimeout(3000);
		
		totalLen = conn.getContentLength();									// 获取服务端发送过来的文件长度
		threadLen = (totalLen + THREAD_AMOUNT - 1) / THREAD_AMOUNT;			// 计算每个线程要下载的长度
		
		// 发送文件大小
		Message msg = new Message();
		msg.getData().putLong("totalLen", totalLen);
		msg.what = 1;
		handler.sendMessage(msg);
		
		if (!dataFile.exists()) {											// 如果本地文件不存在
			RandomAccessFile raf = new RandomAccessFile(dataFile, "rws");	// 在本地创建文件
			raf.setLength(totalLen);										// 设置文件的大小和服务端相同
			raf.close();
		}
		
		if (!tempFile.exists()) {											// 如果临时文件不存在
			RandomAccessFile raf = new RandomAccessFile(tempFile, "rws");	// 创建临时文件, 用来记录每个线程已下载多少
			for (int i = 0; i < THREAD_AMOUNT; i++)							// 按照线程数循环
				raf.writeLong(0);											// 写入每个线程的开始位置(都是从0开始)
			raf.close();
		}
		
		for (int i = 0; i < THREAD_AMOUNT; i++)	// 按照线程数循环
			new DownloadThread(i).start();		// 开启线程, 每个线程将会下载一部分数据到本地文件中
		
		
		begin = System.currentTimeMillis();		// 记录开始时间
	}
	
	private class DownloadThread extends Thread {
		private int id; 	// 用来标记当前线程是下载任务中的第几个线程
		
		public DownloadThread(int id) {
			this.id = id;
		}

		public void run() {

			try {
				RandomAccessFile tempRaf = new RandomAccessFile(tempFile, "rws");		// 用来记录下载进度的临时文件
				tempRaf.seek(id * 8);						// 将指针移动到当前线程的位置(每个线程写1个long值, 占8字节)
				long threadFinish = tempRaf.readLong();		// 读取当前线程已完成了多少
				synchronized(BreakpointDownloader.this) {	// 多个下载线程之间同步
					totalFinish += threadFinish;			// 统计所有线程总共完成了多少
				}
				
				long start = id * threadLen + threadFinish;		// 计算当前线程的起始位置
				long end = id * threadLen + threadLen - 1;		// 计算当前线程的结束位置
				System.out.println("线程" + id + ": " + start + "-" + end);
			
				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
				conn.setConnectTimeout(3000);
				conn.setRequestProperty("Range", "bytes=" + start + "-" + end);		// 设置当前线程下载的范围
				
				InputStream in = conn.getInputStream();								// 获取连接的输入流
				RandomAccessFile dataRaf = new RandomAccessFile(dataFile, "rws");	// 装载数据的本地文件(可以理解为输出流)
				dataRaf.seek(start);												// 设置当前线程保存数据的位置
				
				byte[] buffer = new byte[1024*100];			// 每次拷贝100KB
				int len;
			
					
		
				while ((len = in.read(buffer)) != -1) {
		
						
			
					dataRaf.write(buffer, 0, len);				// 从服务端读取数据, 写到本地文件
					threadFinish += len;						// 每次写入数据之后, 统计当前线程完成了多少
					tempRaf.seek(id * 8);						// 将临时文件的指针指向当前线程的位置
					tempRaf.writeLong(threadFinish);			// 将当前线程完成了多少写入到临时文件
					synchronized(BreakpointDownloader.this) {	// 多个下载线程之间同步
						totalFinish += len;						// 统计所有线程总共完成了多少
						// 发送当前进度
						Message msg = new Message();
						msg.getData().putLong("totalFinish", totalFinish);
						msg.what = 2;
						handler.sendMessage(msg);
					}
					}
				
				dataRaf.close();
				tempRaf.close();
				
				
				System.out.println("线程" + id + "下载完毕");
				if (totalFinish == totalLen) {					// 如果已完成长度等于服务端文件长度(代表下载完成)
					System.out.println("下载完成, 耗时: " + (System.currentTimeMillis() - begin));
					tempFile.delete();							// 删除临时文件
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
	}
}


package com.example.down2;


import cs.service.BreakpointDownloader;
import android.os.Bundle;
import android.os.Handler;
import android.os.StrictMode;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

	private EditText path;
	private ProgressBar downloadPB;
	private TextView percentTV;
	private long totallen;
	
	private Handler handler = new Handler(){
		public void handleMessage(android.os.Message msg) {
			if(msg.what == 1){
				
				totallen = msg.getData().getLong("totalLen");
				downloadPB.setMax((int)totallen);
			}else if(msg.what ==2){
				long totalFinish = msg.getData().getLong("totalFinish");
				downloadPB.setProgress((int)totalFinish);
				percentTV.setText(totalFinish*100/totallen+"%");
			}
		};
	};

	@SuppressLint("NewApi")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		if(android.os.Build.VERSION.SDK_INT>9){
			StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
			StrictMode.setThreadPolicy(policy);
		}
		path = (EditText) this.findViewById(R.id.downpath);
		downloadPB = (ProgressBar) this.findViewById(R.id.downloadPB);
		percentTV = (TextView) this.findViewById(R.id.percentTV);
	}
	
	public void download(View v) {
		switch (v.getId()) {
		case R.id.ok:
			String address = path.getText().toString();
			try {
				new BreakpointDownloader(address, handler).download();
			} catch (Exception e) {
				e.printStackTrace();
				Toast.makeText(getApplicationContext(), "出错了", 1).show();
			} 
			break;

		default:
			break;
		}
	}
}

需要添加的权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


百度网盘保存地址:http://pan.baidu.com/s/1mgoePSk


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值