




已阅读5页,还剩14页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Android使用OKHttp3实现下载(断点续传、显示进度)OKHttp3是如今非常流行的Android网络请求框架,那么如何利用Android实现断点续传呢,今天写了个Demo尝试了一下,感觉还是有点意思准备阶段我们会用到OKHttp3来做网络请求,使用RxJava来实现线程的切换,并且开启Java8来启用Lambda表达式,毕竟RxJava实现线程切换非常方便,而且数据流的形式也非常舒服,同时Lambda和RxJava配合食用味道更佳打开我们的app Module下的build.gradle,代码如下plain view plain copy 在CODE上查看代码片派生到我的代码片apply plugin: com.android.application android compileSdkVersion 24 buildToolsVersion 24.0.3 defaultConfig applicationId com.lanou3g.downdemo minSdkVersion 15 targetSdkVersion 24 versionCode 1 versionName 1.0 testInstrumentationRunner android.support.test.runner.AndroidJUnitRunner /为了开启Java8 jackOptions enabled true; buildTypes release minifyEnabled false proguardFiles getDefaultProguardFile(proguard-android.txt), /开启Java1.8 能够使用lambda表达式 compileOptions sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 dependencies compile fileTree(dir: libs, include: *.jar) androidTestCompile(com.android.support.test.espresso:espresso-core:2.2.2, exclude group: com.android.support, module: support-annotations ) compile com.android.support:appcompat-v7:24.1.1 testCompile junit:junit:4.12 /OKHttp compile com.squareup.okhttp3:okhttp:3.6.0 /RxJava和RxAndroid 用来做线程切换的 compile io.reactivex.rxjava2:rxandroid:2.0.1 compile io.reactivex.rxjava2:rxjava:2.0.1 OKHttp和RxJava,RxAndroid使用的都是最新的版本,并且配置开启了Java8布局文件接着开始书写布局文件html view plain copy 在CODE上查看代码片派生到我的代码片 大概是这个样子的3个ProgressBar就是为了显示进度的,每个ProgressBar对应2个Button,一个是开始下载,一个是暂停(取消)下载,这里需要说明的是,对下载来说暂停和取消没有什么区别,除非当取消的时候,会顺带把临时文件都删除了,在本例里是不区分他俩的.Application我们这里需要用到一些文件路径,有一个全局Context会比较方便, 而Application也是Context的子类,使用它的是最方便的,所以我们写一个类来继承Applicationjava view plain copy 在CODE上查看代码片派生到我的代码片package com.lanou3g.downdemo; import android.app.Application; import android.content.Context; /* * Created by 陈丰尧 on 2017/2/2. */ public class MyApp extends Application public static Context sContext;/全局的Context对象 Override public void onCreate() super.onCreate(); sContext = this; 可以看到,我们就是要获得一个全局的Context对象的我们在AndroidManifest中注册一下我们的Application,同时再把我们所需要的权限给上html view plain copy 在CODE上查看代码片派生到我的代码片 我们只需要一个网络权限,在application标签下,添加name属性,来指向我们的ApplicationDownloadManager接下来是核心代码了,就是我们的DownloadManager,先上代码java view plain copy 在CODE上查看代码片派生到我的代码片package com.lanou3g.downdemo; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.Observable; import io.reactivex.ObservableEmitter; import io.reactivex.ObservableOnSubscribe; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; import okhttp3.Call; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; /* * Created by 陈丰尧 on 2017/2/2. */ public class DownloadManager private static final AtomicReference INSTANCE = new AtomicReference(); private HashMap downCalls;/用来存放各个下载的请求 private OkHttpClient mClient;/OKHttpClient; /获得一个单例类 public static DownloadManager getInstance() for (; ; ) DownloadManager current = INSTANCE.get(); if (current != null) return current; current = new DownloadManager(); if (INSTANCE.compareAndSet(null, current) return current; private DownloadManager() downCalls = new HashMap(); mClient = new OkHttpClient.Builder().build(); /* * 开始下载 * * param url 下载请求的网址 * param downLoadObserver 用来回调的接口 */ public void download(String url, DownLoadObserver downLoadObserver) Observable.just(url) .filter(s - !downCalls.containsKey(s)/call的map已经有了,就证明正在下载,则这次不下载 .flatMap(s - Observable.just(createDownInfo(s) .map(this:getRealFileName)/检测本地文件夹,生成新的文件名 .flatMap(downloadInfo - Observable.create(new DownloadSubscribe(downloadInfo)/下载 .observeOn(AndroidSchedulers.mainThread()/在主线程回调 .subscribeOn(Schedulers.io()/在子线程执行 .subscribe(downLoadObserver);/添加观察者 public void cancel(String url) Call call = downCalls.get(url); if (call != null) call.cancel();/取消 downCalls.remove(url); /* * 创建DownInfo * * param url 请求网址 * return DownInfo */ private DownloadInfo createDownInfo(String url) DownloadInfo downloadInfo = new DownloadInfo(url); long contentLength = getContentLength(url);/获得文件大小 downloadInfo.setTotal(contentLength); String fileName = url.substring(url.lastIndexOf(/); downloadInfo.setFileName(fileName); return downloadInfo; private DownloadInfo getRealFileName(DownloadInfo downloadInfo) String fileName = downloadInfo.getFileName(); long downloadLength = 0, contentLength = downloadInfo.getTotal(); File file = new File(MyApp.sContext.getFilesDir(), fileName); if (file.exists() /找到了文件,代表已经下载过,则获取其长度 downloadLength = file.length(); /之前下载过,需要重新来一个文件 int i = 1; while (downloadLength = contentLength) int dotIndex = fileName.lastIndexOf(.); String fileNameOther; if (dotIndex = -1) fileNameOther = fileName + ( + i + ); else fileNameOther = fileName.substring(0, dotIndex) + ( + i + ) + fileName.substring(dotIndex); File newFile = new File(MyApp.sContext.getFilesDir(), fileNameOther); file = newFile; downloadLength = newFile.length(); i+; /设置改变过的文件名/大小 downloadInfo.setProgress(downloadLength); downloadInfo.setFileName(file.getName(); return downloadInfo; private class DownloadSubscribe implements ObservableOnSubscribe private DownloadInfo downloadInfo; public DownloadSubscribe(DownloadInfo downloadInfo) this.downloadInfo = downloadInfo; Override public void subscribe(ObservableEmitter e) throws Exception String url = downloadInfo.getUrl(); long downloadLength = downloadInfo.getProgress();/已经下载好的长度 long contentLength = downloadInfo.getTotal();/文件的总长度 /初始进度信息 e.onNext(downloadInfo); Request request = new Request.Builder() /确定下载的范围,添加此头,则服务器就可以跳过已经下载好的部分 .addHeader(RANGE, bytes= + downloadLength + - + contentLength) .url(url) .build(); Call call = mClient.newCall(request); downCalls.put(url, call);/把这个添加到call里,方便取消 Response response = call.execute(); File file = new File(MyApp.sContext.getFilesDir(), downloadInfo.getFileName(); InputStream is = null; FileOutputStream fileOutputStream = null; try is = response.body().byteStream(); fileOutputStream = new FileOutputStream(file, true); byte buffer = new byte2048;/缓冲数组2kB int len; while (len = is.read(buffer) != -1) fileOutputStream.write(buffer, 0, len); downloadLength += len; downloadInfo.setProgress(downloadLength); e.onNext(downloadInfo); fileOutputStream.flush(); downCalls.remove(url); finally /关闭IO流 IOUtil.closeAll(is, fileOutputStream); e.onComplete();/完成 /* * 获取下载长度 * * param downloadUrl * return */ private long getContentLength(String downloadUrl) Request request = new Request.Builder() .url(downloadUrl) .build(); try Response response = mClient.newCall(request).execute(); if (response != null & response.isSuccessful() long contentLength = response.body().contentLength(); response.close(); return contentLength = 0 ? DownloadInfo.TOTAL_ERROR : contentLength; catch (IOException e) e.printStackTrace(); return DownloadInfo.TOTAL_ERROR; 代码稍微有点长,关键部位我都加了注释了,我们挑关键地方看看首先我们这个类是单例类,我们下载只需要一个OKHttpClient就足够了,所以我们让构造方法私有,而单例类的获取实例方法就是这个getInstance();当然大家用别的方式实现单例也可以的,然后我们在构造方法里初始化我们的HttpClient,并且初始化一个HashMap,用来放所有的网络请求的,这样当我们取消下载的时候,就可以找到url对应的网络请求然后把它取消掉就可以了接下来就是核心的download方法了,首先是参数,第一个参数url不用多说,就是请求的网址,第二个参数是一个Observer对象,因为我们使用的是RxJava,并且没有特别多复杂的方法,所以就没单独写接口,而是谢了一个Observer对象来作为回调,接下来是DownLoadObserver的代码java view plain copy 在CODE上查看代码片派生到我的代码片package com.lanou3g.downdemo; import io.reactivex.Observer; import io.reactivex.disposables.Disposable; /* * Created by 陈丰尧 on 2017/2/2. */ public abstract class DownLoadObserver implements Observer protected Disposable d;/可以用于取消注册的监听者 protected DownloadInfo downloadInfo; Override public void onSubscribe(Disposable d) this.d = d; Override public void onNext(DownloadInfo downloadInfo) this.downloadInfo = downloadInfo; Override public void onError(Throwable e) e.printStackTrace(); 在RxJava2中 这个Observer有点变化,当注册观察者的时候,会调用onSubscribe方法,而该方法参数就是用来取消注册的,这样的改动可以更灵活的有监听者来取消监听了,我们的进度信息会一直的传送的onNext方法里,这里将下载所需要的内容封了一个类叫DownloadInfojava view plain copy 在CODE上查看代码片派生到我的代码片package com.lanou3g.downdemo; /* * Created by 陈丰尧 on 2017/2/2. * 下载信息 */ public class DownloadInfo public static final long TOTAL_ERROR = -1;/获取进度失败 private String url; private long total; private long progress; private String fileName; public DownloadInfo(String url) this.url = url; public String getUrl() return url; public String getFileName() return fileName; public void setFileName(String fileName) this.fileName = fileName; public long getTotal() return total; public void setTotal(long total) this.total = total; public long getProgress() return progress; public void setProgress(long progress) gress = progress; 这个类就是一些基本信息,total就是需要下载的文件的总大小,而progress就是当前下载的进度了,这样就可以计算出下载的进度信息了接着看DownloadManager的download方法,首先通过url生成一个Observable对象,然后通过filter操作符过滤一下,如果当前正在下载这个url对应的内容,那么就不下载它,接下来调用createDownInfo重新生成Observable对象,这里应该用map也是可以的,createDownInfo这个方法里会调用getContentLength来获取服务器上的文件大小,可以看一下这个方法的代码,java view plain copy 在CODE上查看代码片派生到我的代码片/* * 获取下载长度 * * param downloadUrl * return */ private long getContentLength(String downloadUrl) Request request = new Request.Builder() .url(downloadUrl) .build(); try Response response = mClient.newCall(request).execute(); if (response != null & response.isSuccessful() long contentLength = response.body().contentLength(); response.close(); return contentLength = 0 ? DownloadInfo.TOTAL_ERROR : contentLength; catch (IOException e) e.printStackTrace(); return DownloadInfo.TOTAL_ERROR; 可以看到,其实就是在通过OK进行了一次网络请求,并且从返回的头信息里拿到文件的大小信息,一般这个信息都是可以拿到的,除非下载网址不是直接指向资源文件的,而是自己手写的Servlet,那就得跟后台人员沟通好了.注意,这次网络请求并没有真正的去下载文件,而是请求个大小就结束了,具体原因会在后面真正请求数据的时候解释接着download方法获取完文件大小后,就可以去硬盘里找文件了,这里调用了getRealFileName方法java view plain copy 在CODE上查看代码片派生到我的代码片private DownloadInfo getRealFileName(DownloadInfo downloadInfo) String fileName = downloadInfo.getFileName(); long downloadLength = 0, contentLength = downloadInfo.getTotal(); File file = new File(MyApp.sContext.getFilesDir(), fileName); if (file.exists() /找到了文件,代表已经下载过,则获取其长度 downloadLength = file.length(); /之前下载过,需要重新来一个文件 int i = 1; while (downloadLength = contentLength) int dotIndex = fileName.lastIndexOf(.); String fileNameOther; if (dotIndex = -1) fileNameOther = fileName + ( + i + ); else fileNameOther = fileName.subst
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- BIM在节能减排建筑中的应用探讨
- 100以内三数加减法混合运算综合作业题带答案
- 20以内三个数加减混合运算质量考核模拟题
- 我爱后花园爱的作文(14篇)
- 新能源汽车电池技术解析
- 山川的四季美景写景作文(9篇)
- 高中英语语法趣味课堂:英语动词时态游戏
- 名著导读:名著中的语法与修辞
- 品牌推广战略合作契约
- 含支撑骨架充气结构的变形行为研究
- 2025年度尿素肥料采购合同范本及环保要求解析3篇
- 浙教版八年级下科学第四单元植物与土壤尖子生培优卷及答案
- 2025年中国江苏省餐饮行业发展趋势预测及投资战略研究报告
- 【大学课件】道路工程测量2
- 新材料在绿色制造中的应用及发展趋势分析
- 2025正规离婚协议书样本范文
- 日间手术病房术前宣教
- 高层建筑防火涂料施工标准方案
- 2024年重庆市初中学业水平考试生物试卷含答案
- 设施设备维护保养检测制度流程
- 盐酸装卸车操作规程(3篇)
评论
0/150
提交评论