前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android 网络学习之使用多线程下载,支持断点续传

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

作者头像
DragonKingZhu
发布2022-05-08 16:38:27
5920
发布2022-05-08 16:38:27
举报

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

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

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

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

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

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

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

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

代码语言:javascript
复制
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: 既然已经给每个线程分好了下载区间,那我们就开始下载

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

代码语言:javascript
复制
System.out.println("线程" + i + "的下载区间为:" + startIndex + "---" + endIndex);
							//开启threadCount去下载数据
							new downloadThread(startIndex, endIndex, i).start();
代码语言:javascript
复制
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: 既然下载东西,对用户来说就的知道下载的进度。我们使用进度条显示现在的进度

设置最大进度

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

这是当前的进度

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

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

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

效果图:

接下来实现断点续传:

代码语言:javascript
复制
			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();
				}

在下载时候,先需要创建配置文件,防止下载过程中某些原因导致停止下载,当后续接着下载时,还是会用上次下载的地方接着下载

代码语言:javascript
复制
					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

源码下载地址

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2015-08-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档