Android多线程断点续传
来源:互联网 发布:阿里云服务器上传网站 编辑:程序博客网 时间:2024/05/19 23:55
断点续传
思考:
1.断点续传,就是当一次下载任务由于某些因素中断下载,能够继续上次下载的位置继续下载,不必在从头开始。2.支持多线程,就是把一个下载任务分配给多个线程去下载,每一个线程只下载一部分内容。
分析:
http协议,一次下载请求,就是请求远端服务器一个资源(url),建立TCP链接,把服务器资源写入本地的一个过程。
那么如果要实现断点续传的必要条件:
1.我们能够知道下载资源的大小(多少字节)2.我们可以请求某一部分资源(一个文件的某一段字节数据)3.服务器告诉我们返回的资源总大小和偏移量(从某一位置开始返回数据)
查找http协议规范
http/1.1请求头中有下面相关字段请求头GET /file.zip HTTP/1.1 Range:bytes=680- // 从某一个位置开始请求...响应头HTTP/1.1 206 Partial ContentAccept-Ranges : bytes ///告诉客户端,我们是支持断点传输的Content-Length : //返回文件总长度(少字节)Content-Range :bytes //返回的字节范围...
模拟一次单线程断点续传:
1.客户端发起请求,下载file.zipGET /file.zip HTTP/1.1 2.服务器收到请求返回HTTP/1.1 200 OKAccept-Ranges : bytes ///告诉客户端,我们是支持断点传输的Content-Length : 2000 //返回文件总长度(2000字节)...3.客户端在下载到600字节的时候突然间中断了下载链接,然后再次发起续传请求GET /file.zip HTTP/1.1 Range:bytes=600- // 告诉服务器我从600字节开始下载数据...4.服务器收到请求给予回应HTTP/1.1 206 Partial Content // 注意响应码206,不是200Accept-Ranges : bytes ///告诉客户端,我们是支持断点传输的Content-Length : 1400//返回剩余未下载文件总长度Content-Range :bytes 600-1399//返回的字节范围...多线程的下载其实就是,在第一次请求只拿到要下载文件的总大小,然后计算分配给几个线程,每个线程具体下载哪一段数据,然后在去请求对应的数据。
代码设计与实现:
相关技术介绍:
1.HttpURLConnection
URL url = new URL(mFileInfo.getUrl());//请求的链接HttpURLConnection conn = (HttpURLConnection) url.openConnection();//打开链接conn.setConnectTimeout(5 * 1000);//设置超时时间conn.setRequestMethod("GET");//设置请求方法conn.setRequestProperty("Range", "bytes=" + start + "-" +end);// 设置下载文件开始到结束的位置int code = conn.getResponseCode();//拿到响应码conn.getInputStream();// 拿到输入流
2.RandomAccessFile
RandomAccessFile是不属于InputStream和OutputStream类系的。实际上,除了实现DataInput和DataOutput接口之外(DataInputStream和DataOutputStream也实现了这两个接口),它和这两个类系毫不相干,甚至都没有用InputStream和OutputStream已经准备好的功能;它是一个完全独立的类,所有方法(绝大多数都只属于它自己)都是从零开始写的。这可能是因为RandomAccessFile能在文件里面前后移动,所以它的行为与其它的I/O类有些根本性的不同。总而言之,它是一个直接继承Object的,独立的类。
http://www.android-doc.com/reference/java/io/RandomAccessFile.html
void seek(long offset)//移动到某一位置,多线程下载(每个线程写入位置都不同)
代码设计思路(多线程下载支持断点续传)
1.使用数据库来存储每一个线程信息的下载信息,(线程id,下载的url,起始下载位置,终止下载位置,当前下载进度)2.第一次请求的时候,计算好每一个线程需要下载的数据块,存入数据库,并在每一次写入数据时,修改进度3.还原下载的时候,从数据库拿到这些信息,接着未完成的请求。4.如果当前线程完成自己的任务,就从数据库里面移除
核心代码
获取文件大小
HttpURLConnection conn = null; RandomAccessFile raf = null; try { URL url = new URL(mFileInfo.getUrl()); conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5 * 1000); conn.setRequestMethod("GET"); int code = conn.getResponseCode(); int length = -1; if (code == HttpURLConnection.HTTP_OK) { length = conn.getContentLength(); } //如果文件长度为小于0,表示获取文件失败,直接返回 if (length <= 0) { return; } // 判断文件路径是否存在,不存在创建 File dir = new File(DownloadPath); if (!dir.exists()) { dir.mkdir(); } // 创建本地文件 File file = new File(dir, mFileInfo.getFileName()); raf = new RandomAccessFile(file, "rwd"); raf.setLength(length); // 设置文件长度 mFileInfo.setLength(length); // 将FileInfo对象传给Handler Message msg = Message.obtain(); msg.obj = mFileInfo; msg.what = MSG_INIT; mHandler.sendMessage(msg); } catch (Exception e) { e.printStackTrace(); } finally { if (conn != null) { conn.disconnect(); } try { if (raf != null) { raf.close(); } } catch (IOException e) { e.printStackTrace(); } }
下载线程任务
HttpURLConnection conn = null; RandomAccessFile raf = null; InputStream is = null; try { URL url = new URL(mFileInfo.getUrl()); conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5 * 1000); conn.setRequestMethod("GET"); long start = threadInfo.getStart() + threadInfo.getFinished(); // 设置下载文件开始到结束的位置 conn.setRequestProperty("Range", "bytes=" + start + "-" + threadInfo.getEnd()); File file = new File(DownloadService.DownloadPath, mFileInfo.getFileName()); raf = new RandomAccessFile(file, "rwd"); // 设置文件偏移量 raf.seek(start); mFinished += threadInfo.getFinished(); Log.d("xzq","下载起始位置 = "+start+", mFinished = "+mFinished); Intent intent = new Intent(); intent.setAction(DownloadService.ACTION_UPDATE); int code = conn.getResponseCode(); if (code == HttpURLConnection.HTTP_PARTIAL) { is = conn.getInputStream(); byte[] bt = new byte[1024]; int len = -1; while ((len = is.read(bt)) != -1) { raf.write(bt, 0, len); // 累计整个文件完成进度 mFinished += len; // 累加每个线程完成的进度 threadInfo.setFinished(threadInfo.getFinished() + len); if (mIsPause) { mDao.updateThread(threadInfo.getUrl(), threadInfo.getId(), threadInfo.getFinished()); return; } } } // 标识线程是否执行完毕 isFinished = true; // 判断是否所有线程都执行完毕 checkAllFinished(); } catch (Exception e) { e.printStackTrace(); } finally { if (conn != null) { conn.disconnect(); } try { if (is != null) { is.close(); } if (raf != null) { raf.close(); } } catch (IOException e) { e.printStackTrace(); } }
DEMO下载
完整DEMO下载
阅读全文
0 0
- android多线程断点续传
- android多线程断点续传
- Android多线程断点续传
- android 多线程断点续传下载
- android多线程断点续传
- Android多线程断点续传
- android 多线程断点续传
- Android多线程.断点续传下载
- android 多线程断点续传下载
- Android 多线程断点续传
- Android 多线程下载断点续传
- Android多线程断点续传下载
- android 多线程断点续传
- Android实现多线程断点续传
- Android多线程断点续传下载
- android多线程断点续传下载
- android 多线程下载断点续传
- android多线程断点续传下载
- 怎样“无痛”全局替换字体
- JMETER BEANSHELL SAMPLE 加密
- Spring Boot系列01-Spring Boot + maven 实现Hello World
- mac 中 Apache
- 解决springMVC4下使用@ResponseBody 返回json数据的中文乱码问题
- Android多线程断点续传
- 浅谈JavaScript严格模式
- is-a 和 has-a
- activity1
- DayDreamSDK
- 读CopyOnWriteArrayList有感
- 如何利用tf.add_to_collection、tf.get_collection以及tf.add_n来简化正则项的计算
- PHPCMS V9 如何启用伪静态
- Android进程整理