当前位置:   article > 正文

Android Okhttp断点续传

Android Okhttp断点续传

什么是断点续传

指的是在上传/下载时,将任务(一个文件或压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传/下载,如果碰到网络故障,可以从已经上传/下载的部分开始继续上传/下载未完成的部分,而没有必要从头开始上传/下载。可以节省时间,提高速度。

OkHttp 断点下载

断点下载思路

  • step 1:判断检查本地是否有下载文件,若存在,则获取已下载的文件大小 downloadLength,若不存在,那么本地已下载文件的长度为 0
  • step 2:获取将要下载的文件总大小(HTTP 响应头部的 content-Length)
  • step 3:比对已下载文件大小和将要下载的文件总大小(contentLength),判断要下载的长度
  • step 4:再即将发起下载请求的 HTTP 头部中添加即将下载的文件大小范围(Range: bytes = downloadLength – contentLength)

DownloadTask.java

  1. /**
  2. * String 在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
  3. * Integer 后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
  4. * Integer 当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
  5. */
  6. public class DownloadTask extends AsyncTask<String, Integer, Integer {
  7. public static final int TYPE_SUCCESS = 0;
  8. public static final int TYPE_FAILED = 1;
  9. public static final int TYPE_PAUSED = 2;
  10. public static final int TYPE_CANCELED = 3;
  11. private DownloadListener listener;
  12. private boolean isCanceled = false;
  13. private boolean isPaused = false;
  14. private int lastProgress;
  15. public DownloadTask(DownloadListener listener) {
  16. this.listener = listener;
  17. }
  18. /**
  19. * 这个方法中的所有代码都会在子线程中运行,我们应该在这里处理所有的耗时任务。
  20. *
  21. * @param params
  22. * @return
  23. */
  24. @Override
  25. protected Integer doInBackground(String... params) {
  26. InputStream is = null;
  27. RandomAccessFile savedFile = null;
  28. File file = null;
  29. long downloadLength = 0; //记录已经下载的文件长度
  30. //文件下载地址
  31. String downloadUrl = params[0];
  32. //下载文件的名称
  33. String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
  34. //下载文件存放的目录
  35. String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
  36. //创建一个文件
  37. file = new File(directory + fileName);
  38. if (file.exists()) {
  39. //如果文件存在的话,得到文件的大小
  40. downloadLength = file.length();
  41. }
  42. //得到下载内容的大小
  43. long contentLength = getContentLength(downloadUrl);
  44. if (contentLength == 0) {
  45. return TYPE_FAILED;
  46. } else if (contentLength == downloadLength) {
  47. //已下载字节和文件总字节相等,说明已经下载完成了
  48. return TYPE_SUCCESS;
  49. }
  50. OkHttpClient client = new OkHttpClient();
  51. /**
  52. * HTTP请求是有一个Header的,里面有个Range属性是定义下载区域的,它接收的值是一个区间范围,
  53. * 比如:Range:bytes=0-10000。这样我们就可以按照一定的规则,将一个大文件拆分为若干很小的部分,
  54. * 然后分批次的下载,每个小块下载完成之后,再合并到文件中;这样即使下载中断了,重新下载时,
  55. * 也可以通过文件的字节长度来判断下载的起始点,然后重启断点续传的过程,直到最后完成下载过程。
  56. */
  57. Request request = new Request.Builder()
  58. .addHeader("RANGE", "bytes=" + downloadLength + "-" + contentLength) //断点续传要用到的,指示下载的区间
  59. .url(downloadUrl)
  60. .build();
  61. try {
  62. Response response = client.newCall(request).execute();
  63. if (response != null) {
  64. is = response.body().byteStream();
  65. savedFile = new RandomAccessFile(file, "rw");
  66. savedFile.seek(downloadLength);//跳过已经下载的字节
  67. byte[] b = new byte[1024];
  68. int total = 0;
  69. int len;
  70. while ((len = is.read(b)) != -1) {
  71. if (isCanceled) {
  72. return TYPE_CANCELED;
  73. } else if (isPaused) {
  74. return TYPE_PAUSED;
  75. } else {
  76. total += len;
  77. savedFile.write(b, 0, len);
  78. //计算已经下载的百分比
  79. int progress = (int) ((total + downloadLength) * 100 / contentLength);
  80. //注意:在doInBackground()中是不可以进行UI操作的,如果需要更新UI,比如说反馈当前任务的执行进度,
  81. //可以调用publishProgress()方法完成。
  82. publishProgress(progress);
  83. }
  84. }
  85. response.body().close();
  86. return TYPE_SUCCESS;
  87. }
  88. } catch (IOException e) {
  89. e.printStackTrace();
  90. } finally {
  91. try {
  92. if (is != null) {
  93. is.close();
  94. }
  95. if (savedFile != null) {
  96. savedFile.close();
  97. }
  98. if (isCanceled && file != null) {
  99. file.delete();
  100. }
  101. } catch (Exception e) {
  102. e.printStackTrace();
  103. }
  104. }
  105. return TYPE_FAILED;
  106. }
  107. /**
  108. * 当在后台任务中调用了publishProgress(Progress...)方法之后,onProgressUpdate()方法
  109. * 就会很快被调用,该方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以
  110. * 对界面进行相应的更新。
  111. *
  112. * @param values
  113. */
  114. @Override
  115. protected void onProgressUpdate(Integer... values) {
  116. int progress = values[0];
  117. if (progress lastProgress) {
  118. listener.onProgress(progress);
  119. lastProgress = progress;
  120. }
  121. }
  122. /**
  123. * 当后台任务执行完毕并通过Return语句进行返回时,这个方法就很快被调用。返回的数据会作为参数
  124. * 传递到此方法中,可以利用返回的数据来进行一些UI操作。
  125. *
  126. * @param status
  127. */
  128. @Override
  129. protected void onPostExecute(Integer status) {
  130. switch (status) {
  131. case TYPE_SUCCESS:
  132. listener.onSuccess();
  133. break;
  134. case TYPE_FAILED:
  135. listener.onFailed();
  136. break;
  137. case TYPE_PAUSED:
  138. listener.onPaused();
  139. break;
  140. case TYPE_CANCELED:
  141. listener.onCanceled();
  142. break;
  143. default:
  144. break;
  145. }
  146. }
  147. public void pauseDownload() {
  148. isPaused = true;
  149. }
  150. public void cancelDownload() {
  151. isCanceled = true;
  152. }
  153. /**
  154. * 得到下载内容的完整大小
  155. *
  156. * @param downloadUrl
  157. * @return
  158. */
  159. private long getContentLength(String downloadUrl) {
  160. OkHttpClient client = new OkHttpClient();
  161. Request request = new Request.Builder().url(downloadUrl).build();
  162. try {
  163. Response response = client.newCall(request).execute();
  164. if (response != null && response.isSuccessful()) {
  165. long contentLength = response.body().contentLength();
  166. response.body().close();
  167. return contentLength;
  168. }
  169. } catch (IOException e) {
  170. e.printStackTrace();
  171. }
  172. return 0;
  173. }
  174. }

DownloadListener.java

  1. public class DownloadListener {
  2. /**
  3. * 通知当前的下载进度
  4. * @param progress
  5. */
  6. void onProgress(int progress);
  7. /**
  8. * 通知下载成功
  9. */
  10. void onSuccess();
  11. /**
  12. * 通知下载失败
  13. */
  14. void onFailed();
  15. /**
  16. * 通知下载暂停
  17. */
  18. void onPaused();
  19. /**
  20. * 通知下载取消事件
  21. */
  22. void onCanceled();
  23. }

总结

这下载有很多细节要搞,下载可以用多线程,下载可以断点续传。后面再写吧。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/45681?site
推荐阅读
相关标签
  

闽ICP备14008679号