你必须学会的okhttp——进阶篇
来源:互联网 发布:淘宝上哪家代购是正品 编辑:程序博客网 时间:2024/06/06 00:47
今天上一篇博客刚好在郭神公众号出现了。也有一个多月没写点什么了。今天就继上一次的okhttp继续深入了解把。在你必须学会的okhttp——入门篇中我简单介绍了okhttp的使用方法。不了解可以看完在回来看这篇文章。
好了。话不多说。这次我主要介绍下okhttp如何实现多文件断点下载。
参考博客:
http://blog.csdn.net/KevinsCSDN/article/details/51934274
之前对如何使用okhttp上传与下载我们已经知道该怎么做了。但是如何实现多文件的操作呢?首先,在这边阐述下我做的过程中所遇到的问题。
- 如何存储url对应的当前长度以及总长度
- 如何实现暂停以及续传操作
- 如何用一个info对象实现多文件的下载
- response.body.contentlength与实际长度不一样。(例如我获取的长度是5.5M但他的实际长度是6.7M)
如何存储当前长度以及总长度
我在网上看到很多demo对于这块是用SQLite实现,我觉得完全可以Shareperference来存储,通过他的url来存储对应的当前长度和总长度,有人回说Shareperference不是只能一个建对应一个值么,两个怎么解决。我们可以通过MD5加密的url来存储当前进度。通过MD2加密来存储总进度。
如何实现暂停以及续传操作
我们可以通过okhttp自带的拦截器来实现其效果,具体代码如下:
private Call newCall(long current_length ) { Request request = new Request.Builder() .url(url) .header("RANGE", "bytes=" + current_length + "-") .build(); return client.newCall(request); } public OkHttpClient getProgressClient() { Interceptor interceptor = new Interceptor() { public Response intercept(Chain chain) throws IOException { Response originalResponse = chain.proceed(chain.request()); return originalResponse.newBuilder() .body(originalResponse.body()) .build(); } }; return new OkHttpClient.Builder() .addNetworkInterceptor(interceptor) .build(); }
如何用一个info对象实现多文件的下载
其实在前面我也说了。用Shareperference来实现,具体怎么说的。你的url,存储路径每次都是需要传的,但是为了防止进度冲突(例:明明的A的进度,下载B的时候却用的A的进度)。所以通过传入的url来用Shareperference得到他存储的当前长度与总长度来解决。
response.body.contentlength与实际长度不一样。
其实。。。我也不知道。。百度了好久。得到的答案是在HTTP协议中,消息实体长度和消息实体的传输长度是有区别,比如说gzip压缩下,消息实体长度是压缩前的长度,消息实体的传输长度是gzip压缩后的长度。还有种说法还有种说法是服务器限制问题。不解。总之会导致获取进度的时候,进度值是大于100的。。。
大致的问题和解决方法已经说明了。首先,我们先来看下效果图。
最后上源码,相信你看懂了上面的思路。对于源码的理解就不是很难了。
public class DownLoadSupport { private OkHttpClient okHttpClient; private Call call; @Bean FileSupport fileSupport; @Bean ByteUtils byteUtils; @Bean SharePreferencesUtils sharePreferencesUtils; private MD5Utils md5Utils; public DownLoadSupport() { md5Utils = new MD5Utils(); okHttpClient = getProgressClient(); } public OkHttpClient getProgressClient() { Interceptor interceptor = new Interceptor() { public Response intercept(Chain chain) throws IOException { Response originalResponse = chain.proceed(chain.request()); return originalResponse.newBuilder() .body(originalResponse.body()) .build(); } }; return new OkHttpClient.Builder().addNetworkInterceptor(interceptor).build(); } private Call newCall(HttpDownloadBean httpDownloadBean) { Request request = new Request.Builder().tag(md5Utils.md5(httpDownloadBean.getUrl())) .url(httpDownloadBean.getUrl()).addHeader("Accept-Encoding", "identity") .header("RANGE", "bytes=" + sharePreferencesUtils.get(md5Utils.md5(httpDownloadBean.getUrl()), (long) 0) + "-") .build(); return okHttpClient.newCall(request); } public void download(final HttpDownloadBean httpDownloadBean, final DownloadCallBack callBack) { if (!sharePreferencesUtils.contains(md5Utils.md5(httpDownloadBean.getUrl()))) { sharePreferencesUtils.put(md5Utils.md5(httpDownloadBean.getUrl()), (long) 0); } if (!sharePreferencesUtils.contains(md5Utils.md2(httpDownloadBean.getUrl()))) { sharePreferencesUtils.put(md5Utils.md2(httpDownloadBean.getUrl()), (long) 0); } call = newCall(httpDownloadBean); call.enqueue(new Callback() { public void onFailure(Call call, IOException e) { } public void onResponse(Call call, Response response) throws IOException { writeToSDCard(response, httpDownloadBean, callBack); } }); } public void pause(HttpDownloadBean httpDownloadBean) { for (Call call : okHttpClient.dispatcher().queuedCalls()) { if (call.request().tag().equals(md5Utils.md5(httpDownloadBean.getUrl()))) call.cancel(); } for (Call call : okHttpClient.dispatcher().runningCalls()) { if (call.request().tag().equals(md5Utils.md5(httpDownloadBean.getUrl()))) call.cancel(); } } private void writeToSDCard(Response response, HttpDownloadBean httpDownloadBean, DownloadCallBack callBack) { ResponseBody body = response.body(); InputStream input = body.byteStream(); FileChannel channelOut = null; // 随机访问文件,可以指定断点续传的起始位置 RandomAccessFile randomAccessFile = null; long current = 0; long total = 0; current = (long) sharePreferencesUtils.get(md5Utils.md5(httpDownloadBean.getUrl()), (long) 0); total = (long) sharePreferencesUtils.get(md5Utils.md2(httpDownloadBean.getUrl()), (long) 0); if (total == 0) { total = body.contentLength(); httpDownloadBean.setTotal_length(body.contentLength()); sharePreferencesUtils.put(md5Utils.md2(httpDownloadBean.getUrl()), httpDownloadBean.getTotal_length()); } try { randomAccessFile = new RandomAccessFile(fileSupport.createStorgeFile(httpDownloadBean.getStoragepath(), httpDownloadBean.getFilepath()), "rwd"); //Chanel NIO中的用法,由于RandomAccessFile没有使用缓存策略,直接使用会使得下载速度变慢,亲测缓存下载3.3秒的文件,用普通的RandomAccessFile需要20多秒。 channelOut = randomAccessFile.getChannel(); // 内存映射,直接使用RandomAccessFile,是用其seek方法指定下载的起始位置,使用缓存下载,在这里指定下载位置。 MappedByteBuffer mappedBuffer = channelOut.map(FileChannel.MapMode.READ_WRITE, current, total); byte[] buffer = new byte[1024]; int len; while ((len = input.read(buffer)) != -1) { current += len; if (callBack != null) { callBack.download(byteUtils.getSize(current) + byteUtils.getByte(current), byteUtils.getSize(total) + byteUtils.getByte(total)); callBack.downloadprogress((int) (current * 1.0f / total * 100)); } httpDownloadBean.setCurrent_length(current); if (current >= total) { sharePreferencesUtils.remove(md5Utils.md5(httpDownloadBean.getUrl())); sharePreferencesUtils.remove(md5Utils.md2(httpDownloadBean.getUrl())); } else { sharePreferencesUtils.put(md5Utils.md5(httpDownloadBean.getUrl()), httpDownloadBean.getCurrent_length()); } mappedBuffer.put(buffer, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { try { input.close(); if (channelOut != null) { channelOut.close(); } if (randomAccessFile != null) { randomAccessFile.close(); } } catch (IOException e) { e.printStackTrace(); } } } public interface DownloadCallBack { void download(String current_progress, String total_progress); void downloadprogress(int progress); }}
关于httpdownloadbean:
`public class HttpDownloadBean { private String url = null; private String storagepath = null; private String filepath = null; private long current_length = 0L; private long total_length = 0L; public long getTotal_length() { return total_length; } public void setTotal_length(long total_length) { this.total_length = total_length; } public long getCurrent_length() { return current_length; } public void setCurrent_length(long current_length) { this.current_length = current_length; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getStoragepath() { return storagepath; } public void setStoragepath(String storagepath) { this.storagepath = storagepath; } public String getFilepath() { return filepath; } public void setFilepath(String path) { this.filepath = path; }}
就是这几个数据,通过set和get来设置和获取。
ShareperferenceUtils是关于Shareperference的工具类。filesupport是用来创建文件的。byteutils可以不必理会。
有什么问题可以提出来一起讨论。这应该是年前最后一篇技术博文了。我也该为年后找工作的事件而忙碌了。
- 你必须学会的okhttp——进阶篇
- 你必须学会的okhttp——入门篇
- 教你彻底学会动态规划——进阶篇
- 教你彻底学会动态规划——进阶篇
- 教你彻底学会动态规划——进阶篇
- 你必须学会清洗电脑——提网速
- 你必须学会的几个常用网络测试命令
- 你必须学会的几个常用网络测试命令
- 你必须学会的几个常用网络测试 命令
- 互联网上装逼,你必须学会的十个词汇
- Bootstrap 粘页脚,你必须得学会的简单技能
- 你必须学会的Git入门基本操作
- 你必须学会的Git入门基本操作
- iOS进阶必须学习的知识点之——Runloop
- 程序员必须学会释放压力——对程序员的3条另类忠告
- 程序员必须学会释放压力——对程序员的3条另类忠告
- Linux学习总结(12)——Linux必须学会的60个命令
- Linux学习总结(17)——Linux新手必须学会的12个命令
- C中创建服务源码!
- SpringMVC-redirect重定向传值
- Activity四种启动模式总结
- JavaNio——Channel
- 【Ant】classpath 和classpathref 的区别
- 你必须学会的okhttp——进阶篇
- REST架构-在web service apis
- 树莓派3B键盘映射错误解决
- 让你编程得到升华:开发者需知的10个真理
- soap
- JS基础类型的属性赋值问题
- JNI教程(四)
- Premiere快捷键
- iOS 设置锚点,以锚点为中心缩放