码迷,mamicode.com
首页 > 移动开发 > 详细

Android 网络学习之使用多线程下载,支持断点续传

时间:2015-08-11 18:47:26      阅读:211      评论:0      收藏:0      [点我收藏+]

标签:实现网络下载文件   支持断点续传   

既然本节是学习如何使用多线程下载,那我们先要明白什么是多线程下载,在搞明白什么是多线程下载之前,需要先知道什么是单线程下载。

技术分享

上图就是说明了单线程下载的原来,因此单线程下载速度很慢,因为只有一个任务在干活。

技术分享

这样的话,3个线程下载一个文件,总比1个线程一个文件的速度要快。所以多线程下载数据的速度就快。

既然知道了多线程的下载原理,那我们就分析多个线程是如何下载数据,以及如何保存数据的。

技术分享

知道多线程下载的原理,以及每个线程如何存放数据后,那就开始写代码。

1: 当然先要获取该数据的大小了,这样才知道给每个线程分配多大的下载量

我在服务器上下载一个exe文件名为:wireshark.exe

技术分享

先从服务器上获取该文件的大小,并计算每个线程应该下载的大小区间

public void downloade(View v)
    {
    	Thread thread = new Thread()
    	{
    		//服务器地址
    		String path = "http://192.168.1.123:8080/Wireshark.exe";
    		@Override
    		public void run() {
    			try {
					URL url = new URL(path);
					HttpURLConnection conn = (HttpURLConnection) url.openConnection();
					conn.setRequestMethod("GET");
					conn.setReadTimeout(5000);
					conn.setReadTimeout(5000);
					
					if(conn.getResponseCode() == 200)
					{
						//获取数据的总大小
						int length = conn.getContentLength();
						//每个线程的大小
						int size = length / threadCount;
						
						for(int i = 0; i < threadCount; i++)
						{
							int startIndex = i * size;
							int endIndex = (i + 1)*size - 1;
							//最后一个线程的结束地址为文件总大小-1
							if(i == threadCount - 1)
							{
								endIndex = length - 1;
							}
							
							System.out.println("线程" + i + "的下载区间为:" + startIndex + "---" + endIndex);
						}
						
					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
    		}
    	};
    	
    	thread.start();
    }

打印结果为:

技术分享

可以看到大小是正确的。总的大小为29849552大小

2: 既然已经给每个线程分好了下载区间,那我们就开始下载

在下载开始时,先要在存储设备上分配一个个下载文件一样大小的临时文件,这样可以避免下载过程中出现存储不够。

System.out.println("线程" + i + "的下载区间为:" + startIndex + "---" + endIndex);
							//开启threadCount去下载数据
							new downloadThread(startIndex, endIndex, i).start();

class downloadThread extends Thread{
    	int startIndex;//开始位置
    	int endIndex;//结束位置
    	int threadId;//线程Id
    	
    	//构造方法
		public downloadThread(int startIndex, int endIndex, int threadId) {
			super();
			this.startIndex = startIndex;
			this.endIndex = endIndex;
			this.threadId = threadId;
		}
		
		@Override
		public void run() {
			//这次需要请求要下载的数据了
			URL url;
			try {
				url = new URL(path);
				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
				conn.setRequestMethod("GET");
				conn.setReadTimeout(5000);
				conn.setReadTimeout(5000);
				//设置本次HTTP请求数据的区间
				conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
				//请求部分数据,返回码为206
				if(conn.getResponseCode() == 206)
				{
					//此时取到的流里的数据只有上面给定区间的大小
					InputStream is = conn.getInputStream();
					byte[] b = new byte[1024];
					int len = 0;
					int total = 0;
					
					//再次打开临时文件
					File file = new File(Environment.getExternalStorageDirectory(), filename);
					RandomAccessFile raf = new RandomAccessFile(file, "rwd");
					//把文件的写入位置指定到startindex
					raf.seek(startIndex);
					
					while((len = is.read(b)) != -1)
					{
						raf.write(b, 0, len);
						total += len;
						System.out.println("线程" + threadId + "下载了" + total);
					}
					
					System.out.println("线程" + threadId + "---------------下载完毕-------------------");
					raf.close();
				}
				
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
    }

从打印中可以看到是可以下载成功的。

3: 既然下载东西,对用户来说就的知道下载的进度。我们使用进度条显示现在的进度

设置最大进度

//获取数据的总大小
						int length = conn.getContentLength();
						
						//设置进度条的最大值
						pBar.setMax(length);
						
						//每个线程的大小
						int size = length / threadCount;

这是当前的进度

						raf.write(b, 0, len);
						total += len;
						System.out.println("线程" + threadId + "下载了" + total);
						
						//设置当前进度,是3个线程的总和
						currProgress += len;
						pBar.setProgress(currProgress);

再设置文本显示,当前比例。要使用消息来更新UI

	Handler handler = new Handler()
	{
		public void handleMessage(android.os.Message msg) 
		{
			//显示下载比例,转为为long型,int的时候有时候不够大
			tView.setText((long)pBar.getProgress() * 100 / pBar.getMax() + "%");
		};
	};

效果图:

技术分享

接下来实现断点续传:


			File bakFile = new File(Environment.getExternalStorageDirectory(), threadId + ".txt");
			try {
				//判断文件是否存在
				if(bakFile.exists())
				{
					FileInputStream fis = new FileInputStream(bakFile);
					BufferedReader bReader = new BufferedReader(new InputStreamReader(fis));
					//从进度临时文件中读取出上一次下载的总进度,然后与原本的开始位置相加,得到新的开始位置
					int lastProgress = Integer.parseInt(bReader.readLine());
					startIndex += lastProgress;
					
					//把上次下载的进度显示至进度条
					currProgress += lastProgress;
					pBar.setProgress(currProgress);
					
					//发送消息,让主线程刷新文本进度
					handler.sendEmptyMessage(1);
					fis.close();
				}
在下载时候,先需要创建配置文件,防止下载过程中某些原因导致停止下载,当后续接着下载时,还是会用上次下载的地方接着下载


					while((len = is.read(b)) != -1)
					{
						raf.write(b, 0, len);
						total += len;
						System.out.println("线程" + threadId + "下载了" + total);
						
						//设置当前进度,是3个线程的总和
						currProgress += len;
						pBar.setProgress(currProgress);
						//通过发送消息,更新文本。而进度条不需要通过发消息刷新UI,因为进度条本身就是在别的任务中使用的
						handler.sendEmptyMessage(1);
						
						//将当前的下载进度保存到配置文件中
						RandomAccessFile bakRaFile = new RandomAccessFile(bakFile, "rwd");
						bakRaFile.write((total + "").getBytes());
						bakRaFile.close();
					}
可以正常的支持断点连续下载

技术分享

下载的文件可以正常运行,我将下载文件转为feiq了,因为wireshark有点大


http://download.csdn.net/detail/longwang155069/8991785

源码下载地址


版权声明:本文为博主原创文章,未经博主允许不得转载。

Android 网络学习之使用多线程下载,支持断点续传

标签:实现网络下载文件   支持断点续传   

原文地址:http://blog.csdn.net/longwang155069/article/details/47418545

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!