安卓笔记4--多线程断点续传



多线程断点续传无论在什么平台上都是极为重要的,这部分知识非常重要。老规矩,用一张图来介绍今天的内容。

图片看不清的话可以右键新窗口打开


技术分享

原理的话也很简单多线程就是将一个文件分成不同部分让多个线程直接下载,断点续传使用一个文本记录当前下载量,再开始的时候读取文本下载值就可以了。


直接看代码吧

public class MainActivity extends Activity {
	 String path = "http://192.168.15.77:8080/QQPlayer.exe";
	 int threadCount = 3;
	 int finishedThreadCount = 0;
	 int currentPbProgress;
	private ProgressBar pb;
	private TextView tv;
	
	Handler handler = new Handler(){
		public void handleMessage(android.os.Message msg) {
			tv.setText((long)pb.getProgress() * 100 / pb.getMax() + "%");
		}
	};
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		//进度条用于显示当前下载进度,下载的总字节数作为progress,目标文件的总大小作为max
		pb = (ProgressBar) findViewById(R.id.pb);
		tv = (TextView) findViewById(R.id.tv);
	}

	public void click(View v){
		Thread t = new Thread(){
			@Override
			public void run() {
				try {
					URL url = new URL(path);
					HttpURLConnection conn = (HttpURLConnection) url.openConnection();
					conn.setRequestMethod("GET");
					conn.setConnectTimeout(8000);
					conn.setReadTimeout(8000);
					
					if(conn.getResponseCode() == 200){
						//拿到要下载的文件的总长度
						int length = conn.getContentLength();
						
						//在本地生成一个临时文件,临时文件大小与目标文件大小一致
						File file= new File(Environment.getExternalStorageDirectory(), getFileName(path));
						RandomAccessFile raf = new RandomAccessFile(file, "rwd");
						raf.setLength(length);
						raf.close();
						
						//设置进度条的总进度为目标文件总长
						pb.setMax(length);
						
						//计算每条线程要下载的长度
						int size = length / threadCount;
						//计算每条线程的开始位置与结束位置
						for(int i = 0; i < threadCount; i++){
							int startIndex = i * size;
							int endIndex = (i + 1) * size - 1;
							if( i == threadCount - 1)
								endIndex = length - 1;
							
//							System.out.println("线程" + i + "的下载区间为:" + startIndex + "~" + endIndex);
							new DownLoadThread(startIndex, endIndex, i).start();
						}
						
					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		};
		t.start();
	}
	String getFileName(String path){
		int index = path.lastIndexOf("/");
		return path.substring(index + 1);
	}
	
	class DownLoadThread extends Thread{
		
		int startIndex;
		int endIndex;
		int threadId;
		
		
		public DownLoadThread(int startIndex, int endIndex, int threadId) {
			super();
			this.startIndex = startIndex;
			this.endIndex = endIndex;
			this.threadId = threadId;
		}

		@Override
		public void run() {
			//开启子线程下载目标文件
			try {
				File fileProgess = new File(Environment.getExternalStorageDirectory(), threadId + ".txt");
				int lastTotal = 0;
				//判断文本临时文件是否存在
				if(fileProgess.exists()){
					FileInputStream fis = new FileInputStream(fileProgess);
					BufferedReader br = new BufferedReader(new InputStreamReader(fis));
					//获取上一下载的进度
					lastTotal = Integer.parseInt(br.readLine());
					
					//改变下载的开始位置,已经下载过的数据,就不要再去请求了
					startIndex += lastTotal;
					fis.close();
					
					//把上一次下载的总进度写入进度条
					currentPbProgress += lastTotal;
					pb.setProgress(currentPbProgress);
					handler.sendEmptyMessage(0);
				}
				System.out.println("线程" + threadId + "的最终下载区间为:" + startIndex + "~" + endIndex);
				URL url = new URL(path);
				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
				conn.setRequestMethod("GET");
				conn.setConnectTimeout(8000);
				conn.setReadTimeout(8000);
				//定义请求的数据的范围
				conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
				
				if(conn.getResponseCode() == 206){
					//流里的数据只有startIndex到endIndex区间的数据,并不会包含目标文件所有数据
					InputStream is = conn.getInputStream();
					
					
					byte[] b = new byte[1024];
					int len;
					int total = lastTotal;
					File file= new File(Environment.getExternalStorageDirectory(), getFileName(path));
					RandomAccessFile raf = new RandomAccessFile(file, "rwd");
					//改变往raf中写入数据的开始位置
					raf.seek(startIndex);
					
					while((len = is.read(b)) != -1){
						total += len;
						System.out.println("线程" + threadId + "已下载的字节数为:" + total);
						raf.write(b, 0, len);
						
						//为了完成断点续传,while循环每次下载的进度,都写入一个文本临时文件中
						RandomAccessFile rafProgress = new RandomAccessFile(fileProgess, "rwd");
						rafProgress.write((total + "").getBytes());
						rafProgress.close();
						
						currentPbProgress += len;
						//所有线程每次下载len个长度的字节,都会写到进度条的总进度中
						pb.setProgress(currentPbProgress);
						handler.sendEmptyMessage(0);
					}
					System.out.println("线程" + threadId + "下载完毕---------------------");
					raf.close();
					
					finishedThreadCount++;

					//三个线程全部下载完毕,才去删除文本临时文件
					synchronized (path) {
						if(finishedThreadCount == 3){
							for (int i = 0; i < threadCount; i++) {
								File f = new File(Environment.getExternalStorageDirectory(), i + ".txt");
								if(f.exists())
									f.delete();
							}
							finishedThreadCount = 0;
						}
					}
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。