DownloadManager.java 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. package com.benyanyi.okhttp.download;
  2. import android.content.Context;
  3. import com.benyanyi.okhttp.listener.OnDownLoadObserver;
  4. import java.io.File;
  5. import java.io.FileOutputStream;
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. import java.util.HashMap;
  9. import java.util.concurrent.atomic.AtomicReference;
  10. import io.reactivex.Observable;
  11. import io.reactivex.ObservableEmitter;
  12. import io.reactivex.ObservableOnSubscribe;
  13. import io.reactivex.ObservableSource;
  14. import io.reactivex.android.schedulers.AndroidSchedulers;
  15. import io.reactivex.functions.Function;
  16. import io.reactivex.functions.Predicate;
  17. import io.reactivex.schedulers.Schedulers;
  18. import okhttp3.Call;
  19. import okhttp3.OkHttpClient;
  20. import okhttp3.Request;
  21. import okhttp3.Response;
  22. /**
  23. * @author BenYanYi
  24. * @date 2018/11/29 15:16
  25. * @email ben@yanyi.red
  26. * @overview
  27. */
  28. class DownloadManager {
  29. private static final AtomicReference<DownloadManager> INSTANCE = new AtomicReference<>();
  30. /**
  31. * 用来存放各个下载的请求
  32. */
  33. private HashMap<String, Call> downCalls;
  34. /**
  35. * OKHttpClient;
  36. */
  37. private OkHttpClient mClient;
  38. private Context mContext;
  39. private String suffix = "";//文件名后缀
  40. /**
  41. * 获得一个单例类
  42. *
  43. * @param mContext
  44. * @return
  45. */
  46. public static DownloadManager getInstance(Context mContext) {
  47. for (; ; ) {
  48. DownloadManager current = INSTANCE.get();
  49. if (current != null) {
  50. return current;
  51. }
  52. current = new DownloadManager(mContext);
  53. if (INSTANCE.compareAndSet(null, current)) {
  54. return current;
  55. }
  56. }
  57. }
  58. private DownloadManager(Context mContext) {
  59. downCalls = new HashMap<>();
  60. mClient = new OkHttpClient.Builder().build();
  61. this.mContext = mContext;
  62. }
  63. /**
  64. * 开始下载
  65. *
  66. * @param suffix 文件名后缀
  67. * @param url 下载请求的网址
  68. * @param onDownLoadObserver 用来回调的接口
  69. */
  70. void download(String url, String suffix, OnDownLoadObserver onDownLoadObserver) {
  71. this.suffix = suffix;
  72. this.download(url, onDownLoadObserver);
  73. }
  74. /**
  75. * 开始下载
  76. *
  77. * @param url 下载请求的网址
  78. * @param onDownLoadObserver 用来回调的接口
  79. */
  80. void download(String url, OnDownLoadObserver onDownLoadObserver) {
  81. Observable.just(url)
  82. //call的map已经有了,就证明正在下载,则这次不下载
  83. .filter(new Predicate<String>() {
  84. @Override
  85. public boolean test(String s) throws Exception {
  86. return !downCalls.containsKey(s);
  87. }
  88. })
  89. .flatMap(new Function<String, ObservableSource<?>>() {
  90. @Override
  91. public ObservableSource<?> apply(String s) throws Exception {
  92. return Observable.just(createDownInfo(s));
  93. }
  94. })
  95. //检测本地文件夹,生成新的文件名
  96. .map(new Function<Object, DownloadInfo>() {
  97. @Override
  98. public DownloadInfo apply(Object o) throws Exception {
  99. return getRealFileName((DownloadInfo) o);
  100. }
  101. })
  102. //下载
  103. .flatMap(new Function<DownloadInfo, ObservableSource<DownloadInfo>>() {
  104. @Override
  105. public ObservableSource<DownloadInfo> apply(DownloadInfo downloadInfo) throws Exception {
  106. return Observable.create(new DownloadSubscribe(downloadInfo));
  107. }
  108. })
  109. //在主线程回调
  110. .observeOn(AndroidSchedulers.mainThread())
  111. //在子线程执行
  112. .subscribeOn(Schedulers.io())
  113. //添加观察者
  114. .subscribe(onDownLoadObserver);
  115. }
  116. void cancel(String url) {
  117. Call call = downCalls.get(url);
  118. if (call != null) {
  119. call.cancel();//取消
  120. }
  121. downCalls.remove(url);
  122. }
  123. /**
  124. * 创建DownInfo
  125. *
  126. * @param url 请求网址
  127. * @return DownInfo
  128. */
  129. private DownloadInfo createDownInfo(String url) {
  130. DownloadInfo downloadInfo = new DownloadInfo(url);
  131. //获得文件大小
  132. long contentLength = getContentLength(url);
  133. downloadInfo.setTotal(contentLength);
  134. String fileName = url.substring(url.lastIndexOf("/")) + suffix;
  135. downloadInfo.setFileName(fileName);
  136. return downloadInfo;
  137. }
  138. private DownloadInfo getRealFileName(DownloadInfo downloadInfo) throws Exception {
  139. String fileName = downloadInfo.getFileName();
  140. String savePath = FileUtil.isExistDir(mContext.getPackageName());
  141. long downloadLength = 0, contentLength = downloadInfo.getTotal();
  142. File file = new File(savePath, fileName);
  143. if (file.exists()) {
  144. //找到了文件,代表已经下载过,则获取其长度
  145. downloadLength = file.length();
  146. }
  147. //之前下载过,需要重新来一个文件
  148. int i = 1;
  149. while (downloadLength >= contentLength) {
  150. int dotIndex = fileName.lastIndexOf(".");
  151. String fileNameOther;
  152. if (dotIndex == -1) {
  153. fileNameOther = fileName + "(" + i + ")";
  154. } else {
  155. fileNameOther = fileName.substring(0, dotIndex)
  156. + "(" + i + ")" + fileName.substring(dotIndex);
  157. }
  158. File newFile = new File(savePath, fileNameOther + suffix);
  159. file = newFile;
  160. downloadLength = newFile.length();
  161. i++;
  162. }
  163. //设置改变过的文件名/大小
  164. downloadInfo.setProgress(downloadLength);
  165. downloadInfo.setFileName(file.getName());
  166. downloadInfo.setFile(file);
  167. return downloadInfo;
  168. }
  169. private class DownloadSubscribe implements ObservableOnSubscribe<DownloadInfo> {
  170. private DownloadInfo downloadInfo;
  171. DownloadSubscribe(DownloadInfo downloadInfo) {
  172. this.downloadInfo = downloadInfo;
  173. }
  174. @Override
  175. public void subscribe(ObservableEmitter<DownloadInfo> e) throws Exception {
  176. String url = downloadInfo.getUrl();
  177. //已经下载好的长度
  178. long downloadLength = downloadInfo.getProgress();
  179. //文件的总长度
  180. long contentLength = downloadInfo.getTotal();
  181. //初始进度信息
  182. e.onNext(downloadInfo);
  183. Request request = new Request.Builder()
  184. //确定下载的范围,添加此头,则服务器就可以跳过已经下载好的部分
  185. .addHeader("RANGE", "bytes=" + downloadLength + "-" + contentLength)
  186. .url(url)
  187. .build();
  188. Call call = mClient.newCall(request);
  189. //把这个添加到call里,方便取消
  190. downCalls.put(url, call);
  191. Response response = call.execute();
  192. String savePath = FileUtil.isExistDir(mContext.getPackageName());
  193. File file = new File(savePath, downloadInfo.getFileName());
  194. InputStream is = null;
  195. FileOutputStream fileOutputStream = null;
  196. try {
  197. is = response.body().byteStream();
  198. fileOutputStream = new FileOutputStream(file, true);
  199. //缓冲数组2kB
  200. byte[] buffer = new byte[2048];
  201. int len;
  202. while ((len = is.read(buffer)) != -1) {
  203. fileOutputStream.write(buffer, 0, len);
  204. downloadLength += len;
  205. downloadInfo.setProgress(downloadLength);
  206. e.onNext(downloadInfo);
  207. }
  208. downloadInfo.setFile(file);
  209. fileOutputStream.flush();
  210. downCalls.remove(url);
  211. } finally {
  212. //关闭IO流
  213. IoUtil.closeAll(is, fileOutputStream);
  214. }
  215. e.onComplete();//完成
  216. }
  217. }
  218. /**
  219. * 获取下载长度
  220. *
  221. * @param downloadUrl
  222. * @return
  223. */
  224. private long getContentLength(String downloadUrl) {
  225. Request request = new Request.Builder()
  226. .url(downloadUrl)
  227. .build();
  228. try {
  229. Response response = mClient.newCall(request).execute();
  230. if (response != null && response.isSuccessful()) {
  231. long contentLength = response.body().contentLength();
  232. response.close();
  233. return contentLength == 0 ? DownloadInfo.TOTAL_ERROR : contentLength;
  234. }
  235. } catch (IOException e) {
  236. e.printStackTrace();
  237. }
  238. return DownloadInfo.TOTAL_ERROR;
  239. }
  240. }