安卓中多线程断点续传文件下载核心代码总结
2015-09-27 02:07
471 查看
最近学习了Android多线程断点续传,这里做个小小的总结,我觉得下载的几处关键:创建临时文件并计算每个线程需要下载的部分size,每个线程需要下载的开始和结束startIndex和endIndexe,使用获取的流的长度除以size就得到了每个线程需要下载的部分大小,但是如果除不尽的都留给最后一个线程来下载,
然后就是保证断点续传的机制,使用的是临时文件来存放每个线程下载的进度,每次开始都会判断一下临时文件是否存在,以及记录的对应的线程下载记录,根据记录开始本次下载,判断每个线程是否下载完毕,若果都下载完毕则通过循环删除临时文件,
具体下载过程,再次获取连接,根据206的状态码获取读取流,然后就是Java中的io读写的过程了,需要用到的RandomAccessFile来实现临时文件的创建和写入,简单说是因为每个线程请求的部分不同,因而写入的时候也不是从零开始写的,而RandomAccessFile的seek方法,支持写入时的定位,也就是 在指定位置写入文件,然后几个部分拼凑起来就是完成的要下载的文件了。
此外还有需要注意的细节,比如请求网络的代码要放在子线程中来实现,但是更新进度条棒的操作需要在主线程中来实现,因为主线程中不可以做耗时的操作,另外,将线程当前的下载进度写入到文件的过程需要放在随机写入流的后面来执行,避免意外的中断导致文件中产生了记录而随机访问流还没有来得及写入的情况!
然后就是保证断点续传的机制,使用的是临时文件来存放每个线程下载的进度,每次开始都会判断一下临时文件是否存在,以及记录的对应的线程下载记录,根据记录开始本次下载,判断每个线程是否下载完毕,若果都下载完毕则通过循环删除临时文件,
具体下载过程,再次获取连接,根据206的状态码获取读取流,然后就是Java中的io读写的过程了,需要用到的RandomAccessFile来实现临时文件的创建和写入,简单说是因为每个线程请求的部分不同,因而写入的时候也不是从零开始写的,而RandomAccessFile的seek方法,支持写入时的定位,也就是 在指定位置写入文件,然后几个部分拼凑起来就是完成的要下载的文件了。
此外还有需要注意的细节,比如请求网络的代码要放在子线程中来实现,但是更新进度条棒的操作需要在主线程中来实现,因为主线程中不可以做耗时的操作,另外,将线程当前的下载进度写入到文件的过程需要放在随机写入流的后面来执行,避免意外的中断导致文件中产生了记录而随机访问流还没有来得及写入的情况!
public class Demo { static String path = "http://192.168.1.106:8080/OneKey.exe"; static int THREAD_COUNT = 3; static int count = 0; /** * 分析:多线程下载的思路 * 1.首先要有一个访问的地址,根据该地址建立一次连接, * 2.通过建立的连接返回的状态行来设置本地临时文件的大小,并且根据path获取文件名 * 3.多个线程下载,所以要计算出每个线程需要下载的数据大小,计算每个线程的开始和结束位置,以确定每个线程请求下载的范围。 * 4.打开本地临时文件流,设置每个线程写入本地临时文件的开始位置,和每个线程的请求范围 * 5.获取流,读取,写入到临时文件 */ public static void main(String[] args){ try { URL url = new URL(path); HttpURLConnection openConnection = (HttpURLConnection) url.openConnection(); openConnection.setReadTimeout(8*1000); openConnection.setConnectTimeout(8*1000); openConnection.setRequestMethod("GET"); if(openConnection.getResponseCode() == 200){ int Length = openConnection.getContentLength(); RandomAccessFile raf = new RandomAccessFile(new File(getFileNameFromPath(path)), "rwd"); raf.setLength(Length); raf.close(); //计算每个线程需要下载的部分 int size = Length/THREAD_COUNT; for(int i=0;i<THREAD_COUNT;i++){ int startIndex = i*size; int endIndex = (i+1)*size-1; if(i == THREAD_COUNT-1){ endIndex = Length-1; } System.out.println("线程"+i+"的下载区间为:"+startIndex+"-"+endIndex); new DownLoadThread(startIndex,endIndex,i).start(); } } } catch (Exception e) { e.printStackTrace(); } } static String getFileNameFromPath(String path) { int index = path.lastIndexOf("/"); return path.substring(index+1); } } class DownLoadThread extends Thread{ private int startIndex; private int endIndex; private int ThreadID; /** * 考虑加上断点续传问题,思路就是定义一个变量记录线程当前下载的总量, * 如果线程中断,下次继续下载的时候,就将记录的这个值传给线程,让线程从这个值 * 开始下载,一般保存到临时文件中,下载完毕,文件删除即可。 * @param startIndex * @param endIndex * @param ThreadID */ public DownLoadThread(int startIndex, int endIndex, int ThreadID) { super(); this.startIndex=startIndex; this.endIndex=endIndex; this.ThreadID=ThreadID; } public void run(){ try { int progress = 0; File file = new File(ThreadID+".txt"); if(file.exists()){ FileInputStream in = new FileInputStream(file); BufferedReader br = new BufferedReader(new InputStreamReader(in)); progress = Integer.parseInt(br.readLine()); startIndex +=progress; in.close(); } URL url = new URL(Demo.path); HttpURLConnection openConnection = (HttpURLConnection) url.openConnection(); openConnection.setReadTimeout(8*1000); openConnection.setConnectTimeout(8*1000); openConnection.setRequestMethod("GET"); //设置每个线程需要下载的部分区间 openConnection.setRequestProperty("Range","bytes="+startIndex+"-"+endIndex); if(openConnection.getResponseCode() == 206){ RandomAccessFile raf = new RandomAccessFile(new File(Demo.getFileNameFromPath(Demo.path)), "rwd"); raf.seek(startIndex); InputStream inputStream = openConnection.getInputStream(); byte[] b = new byte[1024]; int len = 0; int total = progress; while((len = inputStream.read(b))!=-1){ raf.write(b, 0, len); total +=len; System.out.println("线程"+ThreadID+"的下载总数为:"+total); //这里就需要生成一个记录文件了 RandomAccessFile rafFile = new RandomAccessFile(file, "rwd"); rafFile.write((total+"").getBytes()); rafFile.close(); } System.out.println("线程"+ThreadID+"下载完毕******************************"); raf.close(); Demo.count++; synchronized (Demo.path) { if(Demo.count == 3){ for(int i=0;i<Demo.count;i++){ File f = new File(i+".txt"); f.delete(); } } } } } catch (Exception e) { e.printStackTrace(); } } }
相关文章推荐
- C++中对sprintf()函数的说明
- Spring——scope详解
- 从c#数组求和说起
- Struts2——ModelDriven运用
- AlgorithmsI Programming Assignment 1: Percolation
- Spring学习笔记--spring+mybatis集成
- 使用Async和Await进行异步编程(C#版 适用于VS2015)
- ubuntu 14.04 下配置 Go 1.51
- 枚举类型
- 查找数组中只出现一次的元素
- C语言打印100 ——200之间的素数
- 流程控制语句
- C++之安全使用数组的4中方法
- MineCraft MOD编程(一)环境搭建
- Spring事务不起作用 问题汇总
- C++ | pair
- Spring IOC
- FTP文件传输协议
- Liunx下安装jdk
- java封装