Commit 5921ea5942cabfb84d5cc71a06c384a9a4ed5206

Authored by 韩亚云
0 parents

文件下载基本功能

Showing 57 changed files with 3154 additions and 0 deletions
.gitignore 0 → 100644
  1 +++ a/.gitignore
  1 +*.iml
  2 +.gradle
  3 +/local.properties
  4 +/.idea
  5 +.DS_Store
  6 +/build
  7 +/captures
  8 +.externalNativeBuild
.idea/caches/build_file_checksums.ser 0 → 100644
No preview for this file type
.idea/caches/gradle_models.ser 0 → 100644
No preview for this file type
.idea/codeStyles/Project.xml 0 → 100644
  1 +++ a/.idea/codeStyles/Project.xml
  1 +<component name="ProjectCodeStyleConfiguration">
  2 + <code_scheme name="Project" version="173">
  3 + <Objective-C-extensions>
  4 + <file>
  5 + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
  6 + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
  7 + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
  8 + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
  9 + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
  10 + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
  11 + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
  12 + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
  13 + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
  14 + </file>
  15 + <class>
  16 + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
  17 + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
  18 + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
  19 + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
  20 + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
  21 + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
  22 + </class>
  23 + <extensions>
  24 + <pair source="cpp" header="h" fileNamingConvention="NONE" />
  25 + <pair source="c" header="h" fileNamingConvention="NONE" />
  26 + </extensions>
  27 + </Objective-C-extensions>
  28 + </code_scheme>
  29 +</component>
0 \ No newline at end of file 30 \ No newline at end of file
.idea/gradle.xml 0 → 100644
  1 +++ a/.idea/gradle.xml
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="GradleSettings">
  4 + <option name="linkedExternalProjectsSettings">
  5 + <GradleProjectSettings>
  6 + <compositeConfiguration>
  7 + <compositeBuild compositeDefinitionSource="SCRIPT" />
  8 + </compositeConfiguration>
  9 + <option name="distributionType" value="DEFAULT_WRAPPED" />
  10 + <option name="externalProjectPath" value="$PROJECT_DIR$" />
  11 + <option name="resolveModulePerSourceSet" value="false" />
  12 + </GradleProjectSettings>
  13 + </option>
  14 + </component>
  15 +</project>
0 \ No newline at end of file 16 \ No newline at end of file
.idea/misc.xml 0 → 100644
  1 +++ a/.idea/misc.xml
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="CMakeSettings">
  4 + <configurations>
  5 + <configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
  6 + </configurations>
  7 + </component>
  8 + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
  9 + <output url="file://$PROJECT_DIR$/build/classes" />
  10 + </component>
  11 + <component name="ProjectType">
  12 + <option name="id" value="Android" />
  13 + </component>
  14 +</project>
0 \ No newline at end of file 15 \ No newline at end of file
.idea/runConfigurations.xml 0 → 100644
  1 +++ a/.idea/runConfigurations.xml
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="RunConfigurationProducerService">
  4 + <option name="ignoredProducers">
  5 + <set>
  6 + <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
  7 + <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
  8 + <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
  9 + </set>
  10 + </option>
  11 + </component>
  12 +</project>
0 \ No newline at end of file 13 \ No newline at end of file
README.md 0 → 100644
  1 +++ a/README.md
  1 +# DownloadFile
  2 +Android OkHttp 下载多个文件 断点下载
  3 +## 介绍
  4 +demo的主要逻辑是,利用okhttp 和 RxJava 在子线程中下载文件,通关观察者模式监听下载的进度,再回调到主线程中,然后利用EventBus 通知页面刷新,更新进度。
  5 +## 效果图
  6 +![download.gif](https://upload-images.jianshu.io/upload_images/3183047-18efb4a2a30a86f2.gif?imageMogr2/auto-orient/strip)
  7 +### step1 导入依赖库
  8 +```Java
  9 +// OKHttp RxJava
  10 + implementation 'com.squareup.okhttp3:okhttp:3.6.0'
  11 + implementation 'io.reactivex.rxjava2:rxjava:2.1.3'
  12 + implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
  13 + // eventbus
  14 + implementation 'org.greenrobot:eventbus:3.0.0'
  15 + implementation 'com.android.support:recyclerview-v7:27.1.1'
  16 +```
  17 +### step 2 定义下载bean
  18 +```Java
  19 +/**
  20 + * Created by zs
  21 + * Date:2018年 09月 12日
  22 + * Time:13:50
  23 + * —————————————————————————————————————
  24 + * About: 下载管理
  25 + * —————————————————————————————————————
  26 + */
  27 +public class DownloadInfo {
  28 +
  29 + /**
  30 + * 下载状态
  31 + */
  32 + public static final String DOWNLOAD = "download";
  33 + public static final String DOWNLOAD_PAUSE = "pause";
  34 + public static final String DOWNLOAD_CANCEL = "cancel";
  35 + public static final String DOWNLOAD_OVER = "over";
  36 + public static final String DOWNLOAD_ERROR = "error";
  37 +
  38 + public static final long TOTAL_ERROR = -1;//获取进度失败
  39 +
  40 + private String url;
  41 + private String fileName;
  42 + private String downloadStatus;
  43 + private long total;
  44 + private long progress;
  45 +
  46 + public DownloadInfo(String url) {
  47 + this.url = url;
  48 + }
  49 +
  50 + public String getUrl() {
  51 + return url;
  52 + }
  53 +
  54 + public String getFileName() {
  55 + return fileName;
  56 + }
  57 +
  58 + public void setFileName(String fileName) {
  59 + this.fileName = fileName;
  60 + }
  61 +
  62 + public long getTotal() {
  63 + return total;
  64 + }
  65 +
  66 + public void setTotal(long total) {
  67 + this.total = total;
  68 + }
  69 +
  70 + public long getProgress() {
  71 + return progress;
  72 + }
  73 +
  74 + public void setProgress(long progress) {
  75 + this.progress = progress;
  76 + }
  77 +
  78 + public String getDownloadStatus() {
  79 + return downloadStatus;
  80 + }
  81 +
  82 + public void setDownloadStatus(String downloadStatus) {
  83 + this.downloadStatus = downloadStatus;
  84 + }
  85 +}
  86 +
  87 +```
  88 +### step 3 下载管理类 也是主要的内容 (DownloadManager)
  89 +```Java
  90 +/**
  91 + * Created by zs
  92 + * Date:2018年 09月 12日
  93 + * Time:13:56
  94 + * —————————————————————————————————————
  95 + * About: 下载管理
  96 + * —————————————————————————————————————
  97 + */
  98 +public class DownloadManager {
  99 +
  100 + private static final AtomicReference<DownloadManager> INSTANCE = new AtomicReference<>();
  101 + private OkHttpClient mClient;
  102 + private HashMap<String, Call> downCalls; //用来存放各个下载的请求
  103 +
  104 + public static DownloadManager getInstance() {
  105 + for (; ; ) {
  106 + DownloadManager current = INSTANCE.get();
  107 + if (current != null) {
  108 + return current;
  109 + }
  110 + current = new DownloadManager();
  111 + if (INSTANCE.compareAndSet(null, current)) {
  112 + return current;
  113 + }
  114 + }
  115 + }
  116 +
  117 + private DownloadManager() {
  118 + downCalls = new HashMap<>();
  119 + mClient = new OkHttpClient.Builder().build();
  120 + }
  121 +
  122 + /**
  123 + * 查看是否在下载任务中
  124 + * @param url
  125 + * @return
  126 + */
  127 + public boolean getDownloadUrl(String url){
  128 + return downCalls.containsKey(url);
  129 + }
  130 +
  131 + /**
  132 + * 开始下载
  133 + *
  134 + * @param url 下载请求的网址
  135 + * @param downLoadObserver 用来回调的接口
  136 + */
  137 + public void download(String url, DownloadObserver downLoadObserver) {
  138 +
  139 + Observable.just(url)
  140 + .filter(new Predicate<String>() {
  141 + @Override
  142 + public boolean test(String s) {
  143 + return !downCalls.containsKey(s);
  144 + }
  145 + }) // 过滤 call的map中已经有了,就证明正在下载,则这次不下载
  146 + .flatMap(new Function<String, ObservableSource<?>>() {
  147 + @Override
  148 + public ObservableSource<?> apply(String s) {
  149 + return Observable.just(createDownInfo(s));
  150 + }
  151 + }) // 生成 DownloadInfo
  152 + .map(new Function<Object, DownloadInfo>() {
  153 + @Override
  154 + public DownloadInfo apply(Object o) {
  155 + return getRealFileName((DownloadInfo)o);
  156 + }
  157 + }) // 如果已经下载,重新命名
  158 + .flatMap(new Function<DownloadInfo, ObservableSource<DownloadInfo>>() {
  159 + @Override
  160 + public ObservableSource<DownloadInfo> apply(DownloadInfo downloadInfo) {
  161 + return Observable.create(new DownloadSubscribe(downloadInfo));
  162 + }
  163 + }) // 下载
  164 + .observeOn(AndroidSchedulers.mainThread()) // 在主线程中回调
  165 + .subscribeOn(Schedulers.io()) // 在子线程中执行
  166 + .subscribe(downLoadObserver); // 添加观察者,监听下载进度
  167 + }
  168 +
  169 + /**
  170 + * 下载取消或者暂停
  171 + * @param url
  172 + */
  173 + public void pauseDownload(String url) {
  174 + Call call = downCalls.get(url);
  175 + if (call != null) {
  176 + call.cancel();//取消
  177 + }
  178 + downCalls.remove(url);
  179 + }
  180 +
  181 + /**
  182 + * 取消下载 删除本地文件
  183 + * @param info
  184 + */
  185 + public void cancelDownload(DownloadInfo info){
  186 + pauseDownload(info.getUrl());
  187 + info.setProgress(0);
  188 + info.setDownloadStatus(DownloadInfo.DOWNLOAD_CANCEL);
  189 + EventBus.getDefault().post(info);
  190 + Constant.deleteFile(info.getFileName());
  191 + }
  192 +
  193 + /**
  194 + * 创建DownInfo
  195 + *
  196 + * @param url 请求网址
  197 + * @return DownInfo
  198 + */
  199 + private DownloadInfo createDownInfo(String url) {
  200 + DownloadInfo downloadInfo = new DownloadInfo(url);
  201 + long contentLength = getContentLength(url);//获得文件大小
  202 + downloadInfo.setTotal(contentLength);
  203 + String fileName = url.substring(url.lastIndexOf("/"));
  204 + downloadInfo.setFileName(fileName);
  205 + return downloadInfo;
  206 + }
  207 +
  208 + /**
  209 + * 如果文件已下载重新命名新文件名
  210 + * @param downloadInfo
  211 + * @return
  212 + */
  213 + private DownloadInfo getRealFileName(DownloadInfo downloadInfo) {
  214 + String fileName = downloadInfo.getFileName();
  215 + long downloadLength = 0, contentLength = downloadInfo.getTotal();
  216 + File path = new File(Constant.FILE_PATH);
  217 + if (!path.exists()) {
  218 + path.mkdir();
  219 + }
  220 + File file = new File(Constant.FILE_PATH, fileName);
  221 + if (file.exists()) {
  222 + //找到了文件,代表已经下载过,则获取其长度
  223 + downloadLength = file.length();
  224 + }
  225 + //之前下载过,需要重新来一个文件
  226 + int i = 1;
  227 + while (downloadLength >= contentLength) {
  228 + int dotIndex = fileName.lastIndexOf(".");
  229 + String fileNameOther;
  230 + if (dotIndex == -1) {
  231 + fileNameOther = fileName + "(" + i + ")";
  232 + } else {
  233 + fileNameOther = fileName.substring(0, dotIndex)
  234 + + "(" + i + ")" + fileName.substring(dotIndex);
  235 + }
  236 + File newFile = new File(Constant.FILE_PATH, fileNameOther);
  237 + file = newFile;
  238 + downloadLength = newFile.length();
  239 + i++;
  240 + }
  241 + //设置改变过的文件名/大小
  242 + downloadInfo.setProgress(downloadLength);
  243 + downloadInfo.setFileName(file.getName());
  244 + return downloadInfo;
  245 + }
  246 +
  247 + private class DownloadSubscribe implements ObservableOnSubscribe<DownloadInfo> {
  248 + private DownloadInfo downloadInfo;
  249 +
  250 + public DownloadSubscribe(DownloadInfo downloadInfo) {
  251 + this.downloadInfo = downloadInfo;
  252 + }
  253 +
  254 + @Override
  255 + public void subscribe(ObservableEmitter<DownloadInfo> e) throws Exception {
  256 + String url = downloadInfo.getUrl();
  257 + long downloadLength = downloadInfo.getProgress();//已经下载好的长度
  258 + long contentLength = downloadInfo.getTotal();//文件的总长度
  259 + //初始进度信息
  260 + e.onNext(downloadInfo);
  261 + Request request = new Request.Builder()
  262 + //确定下载的范围,添加此头,则服务器就可以跳过已经下载好的部分
  263 + .addHeader("RANGE", "bytes=" + downloadLength + "-" + contentLength)
  264 + .url(url)
  265 + .build();
  266 + Call call = mClient.newCall(request);
  267 + downCalls.put(url, call);//把这个添加到call里,方便取消
  268 + Response response = call.execute();
  269 + File file = new File(Constant.FILE_PATH, downloadInfo.getFileName());
  270 + InputStream is = null;
  271 + FileOutputStream fileOutputStream = null;
  272 + try {
  273 + is = response.body().byteStream();
  274 + fileOutputStream = new FileOutputStream(file, true);
  275 + byte[] buffer = new byte[2048];//缓冲数组2kB
  276 + int len;
  277 + while ((len = is.read(buffer)) != -1) {
  278 + fileOutputStream.write(buffer, 0, len);
  279 + downloadLength += len;
  280 + downloadInfo.setProgress(downloadLength);
  281 + e.onNext(downloadInfo);
  282 + }
  283 + fileOutputStream.flush();
  284 + downCalls.remove(url);
  285 + } finally {
  286 + //关闭IO流
  287 + DownloadIO.closeAll(is, fileOutputStream);
  288 +
  289 + }
  290 + e.onComplete();//完成
  291 + }
  292 + }
  293 +
  294 + /**
  295 + * 获取下载长度
  296 + *
  297 + * @param downloadUrl
  298 + * @return
  299 + */
  300 + private long getContentLength(String downloadUrl) {
  301 + Request request = new Request.Builder()
  302 + .url(downloadUrl)
  303 + .build();
  304 + try {
  305 + Response response = mClient.newCall(request).execute();
  306 + if (response != null && response.isSuccessful()) {
  307 + long contentLength = response.body().contentLength();
  308 + response.close();
  309 + return contentLength == 0 ? DownloadInfo.TOTAL_ERROR : contentLength;
  310 + }
  311 + } catch (IOException e) {
  312 + e.printStackTrace();
  313 + }
  314 + return DownloadInfo.TOTAL_ERROR;
  315 + }
  316 +
  317 +}
  318 +```
  319 +这里要多分析一下,下载管理类是单例模式这是必须的,里面定义一个HashMap用来存放所有的下载任务,里面的download()方法,是主要过程
  320 +```Java
  321 +public void download(String url, DownloadObserver downLoadObserver) {
  322 +
  323 + Observable.just(url)
  324 + .filter(new Predicate<String>() {
  325 + @Override
  326 + public boolean test(String s) {
  327 + return !downCalls.containsKey(s);
  328 + }
  329 + }) // 过滤 call的map中已经有了,就证明正在下载,则这次不下载
  330 + .flatMap(new Function<String, ObservableSource<?>>() {
  331 + @Override
  332 + public ObservableSource<?> apply(String s) {
  333 + return Observable.just(createDownInfo(s));
  334 + }
  335 + }) // 生成 DownloadInfo
  336 + .map(new Function<Object, DownloadInfo>() {
  337 + @Override
  338 + public DownloadInfo apply(Object o) {
  339 + return getRealFileName((DownloadInfo)o);
  340 + }
  341 + }) // 如果已经下载,重新命名
  342 + .flatMap(new Function<DownloadInfo, ObservableSource<DownloadInfo>>() {
  343 + @Override
  344 + public ObservableSource<DownloadInfo> apply(DownloadInfo downloadInfo) {
  345 + return Observable.create(new DownloadSubscribe(downloadInfo));
  346 + }
  347 + }) // 下载
  348 + .observeOn(AndroidSchedulers.mainThread()) // 在主线程中回调
  349 + .subscribeOn(Schedulers.io()) // 在子线程中执行
  350 + .subscribe(downLoadObserver); // 添加观察者,监听下载进度
  351 + }
  352 +```
  353 +这里用到的是Rxjava,第一步filter,过滤下载,已经下载的url再点击下载是不会另起下载任务的;第二步flatMap,通过url生成Bean类,这个按需求来设计,也可以直接传一个Bean进来也是可以的;第三步map,如果这个文件已经下载了,再次下载重新命名文件,这也是根据需求改变,如果下载过的文件不需要下载,这就可以省了;第四步flatMap,去下载具体去看下载方法;剩下的就是切换线程和添加观察者了。
  354 +### step4 观察者
  355 +```Java
  356 +/**
  357 + * Created by zs
  358 + * Date:2018年 09月 12日
  359 + * Time:13:50
  360 + * —————————————————————————————————————
  361 + * About: 观察者
  362 + * —————————————————————————————————————
  363 + */
  364 +public class DownloadObserver implements Observer<DownloadInfo> {
  365 +
  366 + public Disposable d;//可以用于取消注册的监听者
  367 + public DownloadInfo downloadInfo;
  368 +
  369 + @Override
  370 + public void onSubscribe(Disposable d) {
  371 + this.d = d;
  372 + }
  373 +
  374 + @Override
  375 + public void onNext(DownloadInfo value) {
  376 + this.downloadInfo = value;
  377 + downloadInfo.setDownloadStatus(DownloadInfo.DOWNLOAD);
  378 + EventBus.getDefault().post(downloadInfo);
  379 + }
  380 +
  381 + @Override
  382 + public void onError(Throwable e) {
  383 + Log.d("My_Log","onError");
  384 + if (DownloadManager.getInstance().getDownloadUrl(downloadInfo.getUrl())){
  385 + DownloadManager.getInstance().pauseDownload(downloadInfo.getUrl());
  386 + downloadInfo.setDownloadStatus(DownloadInfo.DOWNLOAD_ERROR);
  387 + EventBus.getDefault().post(downloadInfo);
  388 + }else{
  389 + downloadInfo.setDownloadStatus(DownloadInfo.DOWNLOAD_PAUSE);
  390 + EventBus.getDefault().post(downloadInfo);
  391 + }
  392 +
  393 + }
  394 +
  395 + @Override
  396 + public void onComplete() {
  397 + Log.d("My_Log","onComplete");
  398 + if (downloadInfo != null){
  399 + downloadInfo.setDownloadStatus(DownloadInfo.DOWNLOAD_OVER);
  400 + EventBus.getDefault().post(downloadInfo);
  401 + }
  402 + }
  403 +}
  404 +
  405 +```
  406 +这里是用到EventBus来通知页面刷新的,当然也可以不用,把订阅者直接写在Activity,但是那样不利于代码的复用,如果多个页面需要进度更新就麻烦了。
  407 +### step5 Activity 和 Adapter
  408 +页面中是一个RecyclerView,交互逻辑不是很多,说一点,Adapter条目的更新,用的notifyItemChanged(i) 每次更新某一条的进度,而不是notifyDataSetChanged()全部刷新,因为调用全部刷新,刷新的频率很高会导致条目中控件的点击事件不好用没有响应,被拦截了,而更新某一条也会有一个问题是条目刷新时会闪动,解决方案是把RecyclerView的刷新动画去掉,这样就解决了。
  409 +```Java
  410 +// 取消item刷新的动画
  411 + ((SimpleItemAnimator)recycler_view.getItemAnimator()).setSupportsChangeAnimations(false);
  412 +```
  413 +```Java
  414 +/**
  415 + * Created by zs
  416 + * Date:2018年 09月 11日
  417 + * Time:18:06
  418 + * —————————————————————————————————————
  419 + * About:
  420 + * —————————————————————————————————————
  421 + */
  422 +public class DownloadAdapter extends RecyclerView.Adapter<DownloadAdapter.UploadHolder> {
  423 +
  424 + private List<DownloadInfo> mdata;
  425 +
  426 + public DownloadAdapter(List<DownloadInfo> mdata) {
  427 + this.mdata = mdata;
  428 + }
  429 +
  430 + /**
  431 + * 更新下载进度
  432 + * @param info
  433 + */
  434 + public void updateProgress(DownloadInfo info){
  435 + for (int i = 0; i < mdata.size(); i++){
  436 + if (mdata.get(i).getUrl().equals(info.getUrl())){
  437 + mdata.set(i,info);
  438 + notifyItemChanged(i);
  439 + break;
  440 + }
  441 + }
  442 + }
  443 +
  444 + @Override
  445 + public UploadHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  446 + View view = View.inflate(parent.getContext(), R.layout.item_download_layout,null);
  447 + return new UploadHolder(view);
  448 + }
  449 +
  450 + @Override
  451 + public void onBindViewHolder(UploadHolder holder, int position) {
  452 +
  453 + final DownloadInfo info = mdata.get(position);
  454 + if (DownloadInfo.DOWNLOAD_CANCEL.equals(info.getDownloadStatus())){
  455 + holder.main_progress.setProgress(0);
  456 + }else if (DownloadInfo.DOWNLOAD_OVER.equals(info.getDownloadStatus())){
  457 + holder.main_progress.setProgress(holder.main_progress.getMax());
  458 + }else {
  459 + if (info.getTotal() == 0){
  460 + holder.main_progress.setProgress(0);
  461 + }else {
  462 + float d = info.getProgress() * holder.main_progress.getMax() / info.getTotal();
  463 + holder.main_progress.setProgress((int) d);
  464 + }
  465 + }
  466 + holder.main_btn_down.setOnClickListener(new View.OnClickListener() {
  467 + @Override
  468 + public void onClick(View view) {
  469 + DownloadManager.getInstance().download(info.getUrl(), new DownloadObserver());
  470 + }
  471 + });
  472 +
  473 + holder.main_btn_pause.setOnClickListener(new View.OnClickListener() {
  474 + @Override
  475 + public void onClick(View view) {
  476 + DownloadManager.getInstance().pauseDownload(info.getUrl());
  477 + }
  478 + });
  479 +
  480 + holder.main_btn_cancel.setOnClickListener(new View.OnClickListener() {
  481 + @Override
  482 + public void onClick(View view) {
  483 + DownloadManager.getInstance().cancelDownload(info);
  484 + }
  485 + });
  486 + }
  487 +
  488 + @Override
  489 + public int getItemCount() {
  490 + return mdata.size();
  491 + }
  492 +
  493 + public class UploadHolder extends RecyclerView.ViewHolder{
  494 +
  495 + private ProgressBar main_progress;
  496 + private Button main_btn_down;
  497 + private Button main_btn_pause;
  498 + private Button main_btn_cancel;
  499 +
  500 + public UploadHolder(View itemView) {
  501 + super(itemView);
  502 + main_progress = itemView.findViewById(R.id.main_progress);
  503 + main_btn_down = itemView.findViewById(R.id.main_btn_down);
  504 + main_btn_pause = itemView.findViewById(R.id.main_btn_pause);
  505 + main_btn_cancel = itemView.findViewById(R.id.main_btn_cancel);
  506 + }
  507 + }
  508 +
  509 +}
  510 +
  511 +```
  512 +### github地址
  513 +https://github.com/QQzs/DownloadFile
  514 +
  515 +### 简书地址
  516 +https://www.jianshu.com/p/4bab2e9c577e
  517 +
  518 +参考地址:
  519 +https://blog.csdn.net/cfy137000/article/details/54838608
  520 +所有内容基本是参考的这篇文章,我只是少有修改,希望给大家有所帮助。
app/.gitignore 0 → 100644
  1 +++ a/app/.gitignore
  1 +/build
app/build.gradle 0 → 100644
  1 +++ a/app/build.gradle
  1 +apply plugin: 'com.android.application'
  2 +apply plugin: 'org.greenrobot.greendao' // apply plugin
  3 +
  4 +android {
  5 + compileSdkVersion 27
  6 + defaultConfig {
  7 + applicationId "com.cnlive.downloadfile"
  8 + minSdkVersion 16
  9 + targetSdkVersion 27
  10 + versionCode 1
  11 + versionName "1.0"
  12 + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
  13 + }
  14 + buildTypes {
  15 + release {
  16 + minifyEnabled false
  17 + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  18 + }
  19 + }
  20 +
  21 + greendao {
  22 + schemaVersion 1 //数据库版本号
  23 + daoPackage 'com.cnlive.downloadfile.database.greenDao.db'
  24 +// 设置DaoMaster、DaoSession、Dao 包名
  25 + targetGenDir 'src/main/java'//设置DaoMaster、DaoSession、Dao目录
  26 + generateTests false //设置为true以自动生成单元测试。
  27 + targetGenDirTests 'src/main/java' //应存储生成的单元测试的基本目录。默认为 src / androidTest / java。
  28 + }
  29 +}
  30 +
  31 +dependencies {
  32 + implementation fileTree(dir: 'libs', include: ['*.jar'])
  33 + implementation 'com.android.support:appcompat-v7:27.1.1'
  34 + implementation 'com.android.support.constraint:constraint-layout:1.1.3'
  35 + testImplementation 'junit:junit:4.12'
  36 + androidTestImplementation 'com.android.support.test:runner:1.0.2'
  37 + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
  38 +
  39 + // OKHttp RxJava
  40 + implementation 'com.squareup.okhttp3:okhttp:3.6.0'
  41 + implementation 'io.reactivex.rxjava2:rxjava:2.1.3'
  42 + implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
  43 + // eventbus
  44 + implementation 'org.greenrobot:eventbus:3.0.0'
  45 + implementation 'com.android.support:recyclerview-v7:27.1.1'
  46 + implementation 'com.google.code.gson:gson:2.8.1'
  47 +
  48 + implementation 'org.greenrobot:greendao:3.2.2'
  49 +
  50 +}
app/proguard-rules.pro 0 → 100644
  1 +++ a/app/proguard-rules.pro
  1 +# Add project specific ProGuard rules here.
  2 +# You can control the set of applied configuration files using the
  3 +# proguardFiles setting in build.gradle.
  4 +#
  5 +# For more details, see
  6 +# http://developer.android.com/guide/developing/tools/proguard.html
  7 +
  8 +# If your project uses WebView with JS, uncomment the following
  9 +# and specify the fully qualified class name to the JavaScript interface
  10 +# class:
  11 +#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
  12 +# public *;
  13 +#}
  14 +
  15 +# Uncomment this to preserve the line number information for
  16 +# debugging stack traces.
  17 +#-keepattributes SourceFile,LineNumberTable
  18 +
  19 +# If you keep the line number information, uncomment this to
  20 +# hide the original source file name.
  21 +#-renamesourcefileattribute SourceFile
app/src/androidTest/java/com/cnlive/downloadfile/ExampleInstrumentedTest.java 0 → 100644
  1 +++ a/app/src/androidTest/java/com/cnlive/downloadfile/ExampleInstrumentedTest.java
  1 +package com.cnlive.downloadfile;
  2 +
  3 +import android.content.Context;
  4 +import android.support.test.InstrumentationRegistry;
  5 +import android.support.test.runner.AndroidJUnit4;
  6 +
  7 +import org.junit.Test;
  8 +import org.junit.runner.RunWith;
  9 +
  10 +import static org.junit.Assert.*;
  11 +
  12 +/**
  13 + * Instrumented test, which will execute on an Android device.
  14 + *
  15 + * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
  16 + */
  17 +@RunWith(AndroidJUnit4.class)
  18 +public class ExampleInstrumentedTest {
  19 + @Test
  20 + public void useAppContext() {
  21 + // Context of the app under test.
  22 + Context appContext = InstrumentationRegistry.getTargetContext();
  23 +
  24 + assertEquals("com.zs.demo.downloadfile", appContext.getPackageName());
  25 + }
  26 +}
app/src/main/AndroidManifest.xml 0 → 100644
  1 +++ a/app/src/main/AndroidManifest.xml
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3 + xmlns:tools="http://schemas.android.com/tools"
  4 + package="com.cnlive.downloadfile">
  5 +
  6 + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  7 + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  8 + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  9 + <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
  10 + tools:ignore="ProtectedPermissions" />
  11 +
  12 + <uses-permission android:name="android.permission.INTERNET"/>
  13 +
  14 + <application
  15 + android:name=".MyApplication"
  16 + android:allowBackup="true"
  17 + android:icon="@mipmap/ic_launcher"
  18 + android:label="@string/app_name"
  19 + android:roundIcon="@mipmap/ic_launcher_round"
  20 + android:supportsRtl="true"
  21 + android:theme="@style/AppTheme">
  22 + <activity android:name=".MainActivity">
  23 + <intent-filter>
  24 + <action android:name="android.intent.action.MAIN" />
  25 +
  26 + <category android:name="android.intent.category.LAUNCHER" />
  27 + </intent-filter>
  28 + </activity>
  29 + <activity android:name=".LimitActivity"/>
  30 + <service android:name=".download.limit.DownLoadService"/>
  31 + </application>
  32 +
  33 +</manifest>
0 \ No newline at end of file 34 \ No newline at end of file
app/src/main/java/com/cnlive/downloadfile/Constant.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/downloadfile/Constant.java
  1 +package com.cnlive.downloadfile;
  2 +
  3 +import android.os.Environment;
  4 +
  5 +import java.io.File;
  6 +
  7 +/**
  8 + * Created by zs
  9 + * Date:2018年 09月 12日
  10 + * Time:13:54
  11 + * —————————————————————————————————————
  12 + * About:
  13 + * —————————————————————————————————————
  14 + */
  15 +public class Constant {
  16 +
  17 + /**
  18 + * 下载路径
  19 + */
  20 + public final static String FILE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath()+"/AAA/";
  21 +
  22 + /**
  23 + * 若文件下载不下来,更换网址
  24 + */
  25 + public final static String URL_1 = "http://files.ibaodian.com/v2/teamfile/1ca447a600580cdcb575ab9348536f38/CM10086_android_V4.8.0_20180708_A0001.apk";
  26 + public final static String URL_2 = "http://files.ibaodian.com/v2/teamfile/f063d3c2c4a32a8143fc4f36be39cfd9/jtyh.patch";
  27 + public final static String URL_3 = "http://files.ibaodian.com/v2/teamfile/482fd8d425d25f3c3fbdb83156a85af1/IMG_20160508_184212.jpg";
  28 + public final static String URL_4 = "http://files.ibaodian.com/v2/teamfile/c0ab1e924a99738a268c137f60f3a6db/IMG_20160525_115133.jpg";
  29 + public final static String URL_5 = "http://files.ibaodian.com/v2/teamfile/5fe13f1385a0112fb75fceed364088e7/IMG_20180818_132629.jpg";
  30 + public final static String URL_6 = "http://files.ibaodian.com/v2/teamfile/da43a96fde668d4c3fd6f89b8da7e20c/5b726910e7bce766b218d0ee.jpg";
  31 + public final static String URL_7 = "http://files.ibaodian.com/v2/teamfile/ac43a96d0f21e83cd3967e60e6775d1d/sf_updata.apk";
  32 + public final static String URL_8 = "http://files.ibaodian.com/v2/teamfile/长城金禧利年金保险菁华版(A计划).pdf";
  33 + public final static String URL_9 = "http://files.ibaodian.com/v2/teamfile/2b1d7f518fbcf467ec9bf748743bea80/D90B2EA927372212B33BB673318AA1A1361024EB20B8493A9B23E8178DF3D001";
  34 +
  35 + /**
  36 + * 删除文件
  37 + *
  38 + * @param fileName
  39 + * @return
  40 + */
  41 + public static boolean deleteFile(String fileName) {
  42 + boolean status;
  43 + SecurityManager checker = new SecurityManager();
  44 + File file = new File(FILE_PATH + fileName);
  45 + if (file.exists()){
  46 + checker.checkDelete(file.toString());
  47 + if (file.isFile()) {
  48 + try {
  49 + file.delete();
  50 + status = true;
  51 + } catch (SecurityException se) {
  52 + se.printStackTrace();
  53 + status = false;
  54 + }
  55 + } else
  56 + status = false;
  57 + }else
  58 + status = false;
  59 + return status;
  60 + }
  61 +
  62 +}
app/src/main/java/com/cnlive/downloadfile/LimitActivity.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/downloadfile/LimitActivity.java
  1 +package com.cnlive.downloadfile;
  2 +
  3 +import android.content.Intent;
  4 +import android.os.Bundle;
  5 +import android.support.annotation.Nullable;
  6 +import android.support.v7.app.AppCompatActivity;
  7 +import android.support.v7.widget.LinearLayoutManager;
  8 +import android.support.v7.widget.RecyclerView;
  9 +import android.support.v7.widget.SimpleItemAnimator;
  10 +import android.util.Log;
  11 +import android.view.View;
  12 +import android.widget.Button;
  13 +import android.widget.Toast;
  14 +
  15 +import com.cnlive.downloadfile.adapter.LimitDownloadAdapter;
  16 +import com.cnlive.downloadfile.download.limit.DownLoadService;
  17 +import com.cnlive.downloadfile.download.limit.DownloadInfo;
  18 +import com.cnlive.downloadfile.download.limit.DownloadLimitManager;
  19 +import com.cnlive.downloadfile.download.limit.DownloadStatus;
  20 +import com.cnlive.downloadfile.download.util.NetWorkUtil;
  21 +
  22 +import org.greenrobot.eventbus.EventBus;
  23 +import org.greenrobot.eventbus.Subscribe;
  24 +import org.greenrobot.eventbus.ThreadMode;
  25 +
  26 +import java.util.ArrayList;
  27 +import java.util.List;
  28 +
  29 +import static com.cnlive.downloadfile.download.util.CheckNetworkStatusChangeListener.Status.TYPE_MOBILE;
  30 +import static com.cnlive.downloadfile.download.util.CheckNetworkStatusChangeListener.Status.TYPE_UN_NETWORK;
  31 +import static com.cnlive.downloadfile.download.util.CheckNetworkStatusChangeListener.Status.TYPE_WIFI;
  32 +
  33 +
  34 +public class LimitActivity extends AppCompatActivity implements View.OnClickListener {
  35 +
  36 + private RecyclerView recycler_view;
  37 + private LimitDownloadAdapter mAdapter;
  38 + private List<DownloadInfo> mData = new ArrayList<>();
  39 + private Button allStartPause, allClear;
  40 +
  41 + @Override
  42 + protected void onCreate(@Nullable Bundle savedInstanceState) {
  43 + super.onCreate(savedInstanceState);
  44 + setContentView(R.layout.activity_recycler);
  45 + EventBus.getDefault().register(this);
  46 + recycler_view = findViewById(R.id.recycler_view);
  47 +
  48 + allStartPause = findViewById(R.id.start_pause);
  49 + allClear = findViewById(R.id.clear);
  50 +
  51 + allStartPause.setOnClickListener(this);
  52 + allClear.setOnClickListener(this);
  53 + if (DownloadLimitManager.getInstance().getCurremtDownload() == null) {
  54 + allStartPause.setText("全部开始");
  55 + } else {
  56 + allStartPause.setText("全部暂停");
  57 + }
  58 + mData = MyApplication.getInstances().getDaoSession().loadAll(DownloadInfo.class);
  59 + mAdapter = new LimitDownloadAdapter(this, mData);
  60 + recycler_view.setLayoutManager(new LinearLayoutManager(this));
  61 + recycler_view.setAdapter(mAdapter);
  62 + // 取消item刷新的动画
  63 + ((SimpleItemAnimator) recycler_view.getItemAnimator()).setSupportsChangeAnimations(false);
  64 +
  65 + mAdapter.setDelectClick(new LimitDownloadAdapter.DelectClick() {
  66 + @Override
  67 + public void onDelectClickListener(DownloadInfo downloadInfo, int position) {
  68 + //删除该下载
  69 + Intent intent = new Intent(LimitActivity.this, DownLoadService.class);
  70 + intent.setAction(DownLoadService.ACTION_DELECT);
  71 + intent.putExtra("fileUrl", downloadInfo);
  72 + startService(intent);
  73 +
  74 + mData.remove(position);
  75 + mAdapter.notifyItemRemoved(position);
  76 + mAdapter.notifyDataSetChanged();
  77 + }
  78 + });
  79 + }
  80 +
  81 + @Subscribe(threadMode = ThreadMode.MAIN)
  82 + public void update(DownloadInfo info) {
  83 + if (DownloadStatus.DOWNLOAD.equals(info.getDownloadStatus())) {
  84 + allStartPause.setText("全部暂停");
  85 + Log.e("ssssss", "update: "+"dowing");
  86 + mAdapter.updateProgress(info);
  87 + } else if (DownloadStatus.DOWNLOAD_OVER.equals(info.getDownloadStatus())) {
  88 + for (int i = 0; i < mData.size(); i++) {
  89 + if (mData.get(i).getUrl().equals(info.getUrl())) {
  90 + mData.set(i, info);
  91 + MyApplication.getInstances().getDaoSession().delete(info);
  92 + mData.remove(i);
  93 + mAdapter.notifyItemRemoved(i);
  94 + mAdapter.notifyDataSetChanged();
  95 + break;
  96 + }
  97 + }
  98 + if (DownloadLimitManager.getInstance().getCurremtDownload() == null) {
  99 + allStartPause.setText("全部开始");
  100 + }
  101 + } else if (DownloadStatus.DOWNLOAD_PAUSE.equals(info.getDownloadStatus())) {
  102 + mAdapter.updateProgress(info);
  103 + boolean isAllPause = true;
  104 + for(int i = 0;i < mData.size();i++){
  105 + if(!mData.get(i).getDownloadStatus().equals(DownloadStatus.DOWNLOAD_PAUSE)){
  106 + isAllPause = false;
  107 + break;
  108 + }
  109 + }
  110 + if(isAllPause){
  111 + allStartPause.setText("全部开始");
  112 + }
  113 + } else if (DownloadStatus.DOWNLOAD_WAIT.equals(info.getDownloadStatus())) {
  114 + mAdapter.updateProgress(info);
  115 + } else if (DownloadStatus.DOWNLOAD_ERROR.equals(info.getDownloadStatus())) {
  116 + Toast.makeText(this, "下载出错", Toast.LENGTH_SHORT).show();
  117 + }
  118 + }
  119 +
  120 + @Override
  121 + protected void onDestroy() {
  122 + super.onDestroy();
  123 + EventBus.getDefault().unregister(this);
  124 + }
  125 +
  126 + @Override
  127 + public void onClick(View v) {
  128 + switch (v.getId()) {
  129 + case R.id.start_pause:
  130 + if(NetWorkUtil.getNetworkConnectionType(LimitActivity.this).equals(TYPE_UN_NETWORK)){
  131 + Toast.makeText(LimitActivity.this,"无网络",Toast.LENGTH_SHORT).show();
  132 + return;
  133 + }
  134 +
  135 + if(NetWorkUtil.getNetworkConnectionType(LimitActivity.this).equals(TYPE_WIFI)){
  136 + allPause();
  137 + return;
  138 + }
  139 +
  140 + if(NetWorkUtil.getNetworkConnectionType(LimitActivity.this).equals(TYPE_MOBILE)){
  141 + //弹框...保存用户习惯,拒绝或者是允许,允许的话是仅允许一次还是都允许
  142 +
  143 + }
  144 +
  145 + break;
  146 + case R.id.clear:
  147 + Intent intent = new Intent(LimitActivity.this, DownLoadService.class);
  148 + intent.setAction(DownLoadService.ACION_CLEAR);
  149 + startService(intent);
  150 + mData.clear();
  151 + mAdapter.notifyDataSetChanged();
  152 + break;
  153 + }
  154 + }
  155 +
  156 + private void allPause(){
  157 + if (allStartPause.getText().toString().equals("全部开始")) {
  158 + allStartPause.setText("全部暂停");
  159 + Log.e("ssssss", "update: "+"全部暂停");
  160 + Intent intent = new Intent(LimitActivity.this, DownLoadService.class);
  161 + intent.setAction(DownLoadService.ACTION_ALLSTART);
  162 + startService(intent);
  163 + for(int i = 0;i < mData.size();i++){
  164 + mData.get(i).setDownloadStatus(DownloadStatus.DOWNLOAD_WAIT);
  165 + }
  166 + mAdapter.notifyDataSetChanged();
  167 + } else {
  168 + //全部暂停,先进行暂停,再开始
  169 + Log.e("ssssss", "update: "+"全部开始");
  170 + Intent intent = new Intent(LimitActivity.this, DownLoadService.class);
  171 + intent.setAction(DownLoadService.ACTION_ALLPAUSE);
  172 + startService(intent);
  173 + for(int i = 0;i < mData.size();i++){
  174 + mData.get(i).setDownloadStatus(DownloadStatus.DOWNLOAD_PAUSE);
  175 + }
  176 + mAdapter.notifyDataSetChanged();
  177 + }
  178 + }
  179 +}
app/src/main/java/com/cnlive/downloadfile/MainActivity.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/downloadfile/MainActivity.java
  1 +package com.cnlive.downloadfile;
  2 +
  3 +import android.content.Intent;
  4 +import android.os.Bundle;
  5 +import android.support.v4.app.ActivityCompat;
  6 +import android.support.v7.app.AppCompatActivity;
  7 +import android.view.View;
  8 +import android.widget.Toast;
  9 +
  10 +import com.cnlive.downloadfile.download.limit.DownLoadService;
  11 +import com.cnlive.downloadfile.download.limit.DownloadInfo;
  12 +import com.cnlive.downloadfile.download.limit.DownloadLimitManager;
  13 +import com.cnlive.downloadfile.download.limit.DownloadStatus;
  14 +import com.cnlive.downloadfile.database.greenDao.db.DownloadInfoDao;
  15 +import com.cnlive.downloadfile.download.util.NetWorkUtil;
  16 +
  17 +import static com.cnlive.downloadfile.download.util.CheckNetworkStatusChangeListener.Status.TYPE_MOBILE;
  18 +import static com.cnlive.downloadfile.download.util.CheckNetworkStatusChangeListener.Status.TYPE_UN_NETWORK;
  19 +import static com.cnlive.downloadfile.download.util.CheckNetworkStatusChangeListener.Status.TYPE_WIFI;
  20 +
  21 +
  22 +public class MainActivity extends AppCompatActivity {
  23 +
  24 +// private ThreadDAO threadDAO;
  25 + private static final int REQUEST_EXTERNAL_STORAGE = 1;
  26 + private static String[] PERMISSIONS_STORAGE = {"android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE"};
  27 +
  28 + @Override
  29 + protected void onCreate(Bundle savedInstanceState) {
  30 + super.onCreate(savedInstanceState);
  31 + setContentView(R.layout.activity_main);
  32 +// threadDAO = new ThreadDAOImpl(this);
  33 + ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
  34 + }
  35 +
  36 + public void singleDownload(View view) {
  37 +// Intent intent = new Intent(this,SingleActivity.class);
  38 +// startActivity(intent);
  39 + }
  40 +
  41 + public void multipleDownload(View view) {
  42 + if(NetWorkUtil.getNetworkConnectionType(MainActivity.this).equals(TYPE_UN_NETWORK)){
  43 + Toast.makeText(MainActivity.this,"无网络",Toast.LENGTH_SHORT).show();
  44 + return;
  45 + }
  46 +
  47 + if(NetWorkUtil.getNetworkConnectionType(MainActivity.this).equals(TYPE_WIFI)){
  48 + DownloadInfoDao downloadInfoDao = MyApplication.getInstances().getDaoSession().getDownloadInfoDao();
  49 + downloadInfoDao.insertOrReplace(new DownloadInfo(5,Constant.URL_6, "", DownloadStatus.DOWNLOAD_WAIT, 0, 0,""));
  50 + downloadInfoDao.insertOrReplace(new DownloadInfo(6,Constant.URL_7, "", DownloadStatus.DOWNLOAD_WAIT, 0, 0,""));
  51 + downloadInfoDao.insertOrReplace(new DownloadInfo(7,Constant.URL_8, "", DownloadStatus.DOWNLOAD_WAIT, 0, 0,""));
  52 + downloadInfoDao.insertOrReplace(new DownloadInfo(8,Constant.URL_9, "", DownloadStatus.DOWNLOAD_WAIT, 0, 0,""));
  53 + if(DownloadLimitManager.getInstance().getCurremtDownload() == null){
  54 + DownloadInfo downloadInfo = new DownloadInfo(5,Constant.URL_6, "", DownloadStatus.DOWNLOAD, 0, 0,"");
  55 + Intent intent = new Intent(this, DownLoadService.class);
  56 + intent.setAction(DownLoadService.ACTION_START);
  57 + intent.putExtra("fileUrl", downloadInfo);
  58 + startService(intent);
  59 + }
  60 + return;
  61 + }
  62 +
  63 + if(NetWorkUtil.getNetworkConnectionType(MainActivity.this).equals(TYPE_MOBILE)){
  64 + //弹框...保存用户习惯,拒绝或者是允许,允许的话是仅允许一次还是都允许
  65 +
  66 + }
  67 + }
  68 +
  69 + public void addDownload(View view){
  70 + if(NetWorkUtil.getNetworkConnectionType(MainActivity.this).equals(TYPE_UN_NETWORK)){
  71 + Toast.makeText(MainActivity.this,"无网络",Toast.LENGTH_SHORT).show();
  72 + return;
  73 + }
  74 +
  75 + if(NetWorkUtil.getNetworkConnectionType(MainActivity.this).equals(TYPE_WIFI)){
  76 + initData();
  77 + return;
  78 + }
  79 +
  80 + if(NetWorkUtil.getNetworkConnectionType(MainActivity.this).equals(TYPE_MOBILE)){
  81 + //弹框...保存用户习惯,拒绝或者是允许,允许的话是仅允许一次还是都允许
  82 +
  83 + }
  84 + }
  85 +
  86 + private void initData() {
  87 + DownloadInfoDao downloadInfoDao = MyApplication.getInstances().getDaoSession().getDownloadInfoDao();
  88 + downloadInfoDao.insertOrReplace(new DownloadInfo(0,Constant.URL_1, "", DownloadStatus.DOWNLOAD_WAIT, 0, 0,""));
  89 + downloadInfoDao.insertOrReplace(new DownloadInfo(1,Constant.URL_2, "", DownloadStatus.DOWNLOAD_WAIT, 0, 0,""));
  90 + downloadInfoDao.insertOrReplace(new DownloadInfo(2,Constant.URL_3, "", DownloadStatus.DOWNLOAD_WAIT, 0, 0,""));
  91 + downloadInfoDao.insertOrReplace(new DownloadInfo(3,Constant.URL_4, "", DownloadStatus.DOWNLOAD_WAIT, 0, 0,""));
  92 + downloadInfoDao.insertOrReplace(new DownloadInfo(4,Constant.URL_5, "", DownloadStatus.DOWNLOAD_WAIT, 0, 0,""));
  93 + if(DownloadLimitManager.getInstance().getCurremtDownload() == null){
  94 + DownloadInfo downloadInfo = new DownloadInfo(0,Constant.URL_1, "", DownloadStatus.DOWNLOAD, 0, 0,"");
  95 + Intent intent = new Intent(this, DownLoadService.class);
  96 + intent.setAction(DownLoadService.ACTION_START);
  97 + intent.putExtra("fileUrl", downloadInfo);
  98 + startService(intent);
  99 + }
  100 + }
  101 +
  102 + public void limitDownload(View view) {
  103 + Intent intent = new Intent(this, LimitActivity.class);
  104 + startActivity(intent);
  105 + }
  106 +}
app/src/main/java/com/cnlive/downloadfile/MyApplication.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/downloadfile/MyApplication.java
  1 +package com.cnlive.downloadfile;
  2 +
  3 +import android.app.Application;
  4 +import android.database.sqlite.SQLiteDatabase;
  5 +
  6 +import com.cnlive.downloadfile.database.greenDao.db.DaoMaster;
  7 +import com.cnlive.downloadfile.database.greenDao.db.DaoSession;
  8 +
  9 +public class MyApplication extends Application {
  10 +
  11 + private static MyApplication application;
  12 +
  13 + @Override
  14 + public void onCreate() {
  15 + super.onCreate();
  16 + application = this;
  17 + initGreenDao();
  18 + }
  19 +
  20 + public static MyApplication getInstances() {
  21 + return application;
  22 + }
  23 +
  24 +
  25 + /**
  26 + * 初始化GreenDao,直接在Application中进行初始化操作
  27 + */
  28 + private void initGreenDao() {
  29 + DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "aserbao.db");
  30 + SQLiteDatabase db = helper.getWritableDatabase();
  31 + DaoMaster daoMaster = new DaoMaster(db);
  32 + daoSession = daoMaster.newSession();
  33 + }
  34 +
  35 + private DaoSession daoSession;
  36 + public DaoSession getDaoSession() {
  37 + return daoSession;
  38 + }
  39 +}
app/src/main/java/com/cnlive/downloadfile/adapter/LimitDownloadAdapter.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/downloadfile/adapter/LimitDownloadAdapter.java
  1 +package com.cnlive.downloadfile.adapter;
  2 +
  3 +import android.content.Context;
  4 +import android.content.Intent;
  5 +import android.support.v7.widget.RecyclerView;
  6 +import android.view.View;
  7 +import android.view.ViewGroup;
  8 +import android.widget.Button;
  9 +import android.widget.ProgressBar;
  10 +import android.widget.TextView;
  11 +
  12 +import com.cnlive.downloadfile.R;
  13 +import com.cnlive.downloadfile.download.limit.DownLoadService;
  14 +import com.cnlive.downloadfile.download.limit.DownloadInfo;
  15 +import com.cnlive.downloadfile.download.limit.DownloadStatus;
  16 +
  17 +import java.util.List;
  18 +
  19 +public class LimitDownloadAdapter extends RecyclerView.Adapter<LimitDownloadAdapter.UploadHolder> {
  20 +
  21 + private Context mContext;
  22 + private List<DownloadInfo> mdata;
  23 + private DelectClick delectClick;
  24 +
  25 +
  26 + public DelectClick getDelectClick() {
  27 + return delectClick;
  28 + }
  29 +
  30 + public void setDelectClick(DelectClick delectClick) {
  31 + this.delectClick = delectClick;
  32 + }
  33 +
  34 + public LimitDownloadAdapter(Context context , List<DownloadInfo> mdata) {
  35 + this.mContext = context;
  36 + this.mdata = mdata;
  37 + }
  38 +
  39 + /**
  40 + * 更新下载进度
  41 + * @param info
  42 + */
  43 + public void updateProgress(DownloadInfo info){
  44 + for (int i = 0; i < mdata.size(); i++){
  45 + if (mdata.get(i).getUrl().equals(info.getUrl())){
  46 + mdata.set(i,info);
  47 + notifyItemChanged(i);
  48 + break;
  49 + }
  50 + }
  51 + }
  52 +
  53 + @Override
  54 + public UploadHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  55 + View view = View.inflate(parent.getContext(), R.layout.item_download_layout,null);
  56 + return new UploadHolder(view);
  57 + }
  58 +
  59 + @Override
  60 + public void onBindViewHolder(final UploadHolder holder, final int position) {
  61 +
  62 + final DownloadInfo info = mdata.get(position);
  63 + holder.name.setText(info.getUrl().substring(info.getUrl().length()-5,info.getUrl().length()));
  64 + if (DownloadStatus.DOWNLOAD_WAIT.equals(info.getDownloadStatus())){
  65 + holder.main_btn_down.setText("待下载");
  66 + }else if (DownloadStatus.DOWNLOAD_PAUSE.equals(info.getDownloadStatus())){
  67 + holder.main_btn_down.setText("已暂停");
  68 + }
  69 +
  70 + if (info.getTotal() == 0){
  71 + holder.main_progress.setProgress(0);
  72 + }else {
  73 + float d = info.getProgress() * holder.main_progress.getMax() / info.getTotal();
  74 + holder.main_progress.setProgress((int) d);
  75 + if(DownloadStatus.DOWNLOAD.equals(info.getDownloadStatus())) holder.main_btn_down.setText("下载中");
  76 + }
  77 + holder.main_btn_down.setOnClickListener(new View.OnClickListener() {
  78 + @Override
  79 + public void onClick(View view) {
  80 + if(holder.main_btn_down.getText().toString().equals("下载中")){//暂停
  81 + Intent intent = new Intent(mContext, DownLoadService.class);
  82 + intent.setAction(DownLoadService.ACTION_PAUSE);
  83 + intent.putExtra("fileUrl", info);
  84 + mContext.startService(intent);
  85 + }else if(holder.main_btn_down.getText().toString().equals("已暂停")){
  86 + Intent intent = new Intent(mContext, DownLoadService.class);
  87 + intent.setAction(DownLoadService.ACTION_WAITING);
  88 + intent.putExtra("fileUrl", info);
  89 + mContext.startService(intent);
  90 + }else {
  91 + Intent intent = new Intent(mContext, DownLoadService.class);
  92 + intent.setAction(DownLoadService.ACTION_START);
  93 + intent.putExtra("fileUrl", info);
  94 + mContext.startService(intent);
  95 + }
  96 + }
  97 + });
  98 +
  99 + holder.main_btn_cancel.setOnClickListener(new View.OnClickListener() {
  100 + @Override
  101 + public void onClick(View view) {
  102 + if(delectClick != null){
  103 + delectClick.onDelectClickListener(info,position);
  104 + }
  105 + }
  106 + });
  107 + }
  108 +
  109 + @Override
  110 + public int getItemCount() {
  111 + return mdata.size();
  112 + }
  113 +
  114 + public class UploadHolder extends RecyclerView.ViewHolder{
  115 +
  116 + private ProgressBar main_progress;
  117 + private Button main_btn_down;
  118 + private TextView name;
  119 + private Button main_btn_cancel;
  120 +
  121 + public UploadHolder(View itemView) {
  122 + super(itemView);
  123 + main_progress = itemView.findViewById(R.id.main_progress);
  124 + main_btn_down = itemView.findViewById(R.id.main_btn_down);
  125 + name = itemView.findViewById(R.id.name);
  126 + main_btn_cancel = itemView.findViewById(R.id.main_btn_cancel);
  127 + }
  128 + }
  129 +
  130 + public interface DelectClick{
  131 + void onDelectClickListener(DownloadInfo downloadInfo,int position);
  132 + }
  133 +}
app/src/main/java/com/cnlive/downloadfile/database/greenDao/db/DaoMaster.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/downloadfile/database/greenDao/db/DaoMaster.java
  1 +package com.cnlive.downloadfile.database.greenDao.db;
  2 +
  3 +import android.content.Context;
  4 +import android.database.sqlite.SQLiteDatabase;
  5 +import android.database.sqlite.SQLiteDatabase.CursorFactory;
  6 +import android.util.Log;
  7 +
  8 +import org.greenrobot.greendao.AbstractDaoMaster;
  9 +import org.greenrobot.greendao.database.StandardDatabase;
  10 +import org.greenrobot.greendao.database.Database;
  11 +import org.greenrobot.greendao.database.DatabaseOpenHelper;
  12 +import org.greenrobot.greendao.identityscope.IdentityScopeType;
  13 +
  14 +
  15 +// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
  16 +/**
  17 + * Master of DAO (schema version 1): knows all DAOs.
  18 + */
  19 +public class DaoMaster extends AbstractDaoMaster {
  20 + public static final int SCHEMA_VERSION = 1;
  21 +
  22 + /** Creates underlying database table using DAOs. */
  23 + public static void createAllTables(Database db, boolean ifNotExists) {
  24 + DownloadInfoDao.createTable(db, ifNotExists);
  25 + }
  26 +
  27 + /** Drops underlying database table using DAOs. */
  28 + public static void dropAllTables(Database db, boolean ifExists) {
  29 + DownloadInfoDao.dropTable(db, ifExists);
  30 + }
  31 +
  32 + /**
  33 + * WARNING: Drops all table on Upgrade! Use only during development.
  34 + * Convenience method using a {@link DevOpenHelper}.
  35 + */
  36 + public static DaoSession newDevSession(Context context, String name) {
  37 + Database db = new DevOpenHelper(context, name).getWritableDb();
  38 + DaoMaster daoMaster = new DaoMaster(db);
  39 + return daoMaster.newSession();
  40 + }
  41 +
  42 + public DaoMaster(SQLiteDatabase db) {
  43 + this(new StandardDatabase(db));
  44 + }
  45 +
  46 + public DaoMaster(Database db) {
  47 + super(db, SCHEMA_VERSION);
  48 + registerDaoClass(DownloadInfoDao.class);
  49 + }
  50 +
  51 + public DaoSession newSession() {
  52 + return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
  53 + }
  54 +
  55 + public DaoSession newSession(IdentityScopeType type) {
  56 + return new DaoSession(db, type, daoConfigMap);
  57 + }
  58 +
  59 + /**
  60 + * Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} -
  61 + */
  62 + public static abstract class OpenHelper extends DatabaseOpenHelper {
  63 + public OpenHelper(Context context, String name) {
  64 + super(context, name, SCHEMA_VERSION);
  65 + }
  66 +
  67 + public OpenHelper(Context context, String name, CursorFactory factory) {
  68 + super(context, name, factory, SCHEMA_VERSION);
  69 + }
  70 +
  71 + @Override
  72 + public void onCreate(Database db) {
  73 + Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
  74 + createAllTables(db, false);
  75 + }
  76 + }
  77 +
  78 + /** WARNING: Drops all table on Upgrade! Use only during development. */
  79 + public static class DevOpenHelper extends OpenHelper {
  80 + public DevOpenHelper(Context context, String name) {
  81 + super(context, name);
  82 + }
  83 +
  84 + public DevOpenHelper(Context context, String name, CursorFactory factory) {
  85 + super(context, name, factory);
  86 + }
  87 +
  88 + @Override
  89 + public void onUpgrade(Database db, int oldVersion, int newVersion) {
  90 + Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
  91 + dropAllTables(db, true);
  92 + onCreate(db);
  93 + }
  94 + }
  95 +
  96 +}
app/src/main/java/com/cnlive/downloadfile/database/greenDao/db/DaoSession.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/downloadfile/database/greenDao/db/DaoSession.java
  1 +package com.cnlive.downloadfile.database.greenDao.db;
  2 +
  3 +import java.util.Map;
  4 +
  5 +import org.greenrobot.greendao.AbstractDao;
  6 +import org.greenrobot.greendao.AbstractDaoSession;
  7 +import org.greenrobot.greendao.database.Database;
  8 +import org.greenrobot.greendao.identityscope.IdentityScopeType;
  9 +import org.greenrobot.greendao.internal.DaoConfig;
  10 +
  11 +import com.cnlive.downloadfile.download.limit.DownloadInfo;
  12 +
  13 +import com.cnlive.downloadfile.database.greenDao.db.DownloadInfoDao;
  14 +
  15 +// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
  16 +
  17 +/**
  18 + * {@inheritDoc}
  19 + *
  20 + * @see org.greenrobot.greendao.AbstractDaoSession
  21 + */
  22 +public class DaoSession extends AbstractDaoSession {
  23 +
  24 + private final DaoConfig downloadInfoDaoConfig;
  25 +
  26 + private final DownloadInfoDao downloadInfoDao;
  27 +
  28 + public DaoSession(Database db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig>
  29 + daoConfigMap) {
  30 + super(db);
  31 +
  32 + downloadInfoDaoConfig = daoConfigMap.get(DownloadInfoDao.class).clone();
  33 + downloadInfoDaoConfig.initIdentityScope(type);
  34 +
  35 + downloadInfoDao = new DownloadInfoDao(downloadInfoDaoConfig, this);
  36 +
  37 + registerDao(DownloadInfo.class, downloadInfoDao);
  38 + }
  39 +
  40 + public void clear() {
  41 + downloadInfoDaoConfig.clearIdentityScope();
  42 + }
  43 +
  44 + public DownloadInfoDao getDownloadInfoDao() {
  45 + return downloadInfoDao;
  46 + }
  47 +
  48 +}
app/src/main/java/com/cnlive/downloadfile/database/greenDao/db/DownloadInfoDao.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/downloadfile/database/greenDao/db/DownloadInfoDao.java
  1 +package com.cnlive.downloadfile.database.greenDao.db;
  2 +
  3 +import android.database.Cursor;
  4 +import android.database.sqlite.SQLiteStatement;
  5 +
  6 +import org.greenrobot.greendao.AbstractDao;
  7 +import org.greenrobot.greendao.Property;
  8 +import org.greenrobot.greendao.internal.DaoConfig;
  9 +import org.greenrobot.greendao.database.Database;
  10 +import org.greenrobot.greendao.database.DatabaseStatement;
  11 +
  12 +import com.cnlive.downloadfile.download.limit.DownloadInfo;
  13 +
  14 +// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
  15 +/**
  16 + * DAO for table "DOWNLOAD_INFO".
  17 +*/
  18 +public class DownloadInfoDao extends AbstractDao<DownloadInfo, Long> {
  19 +
  20 + public static final String TABLENAME = "DOWNLOAD_INFO";
  21 +
  22 + /**
  23 + * Properties of entity DownloadInfo.<br/>
  24 + * Can be used for QueryBuilder and for referencing column names.
  25 + */
  26 + public static class Properties {
  27 + public final static Property Id = new Property(0, long.class, "id", true, "_id");
  28 + public final static Property Url = new Property(1, String.class, "url", false, "URL");
  29 + public final static Property FileName = new Property(2, String.class, "fileName", false, "FILE_NAME");
  30 + public final static Property DownloadStatus = new Property(3, String.class, "downloadStatus", false, "DOWNLOAD_STATUS");
  31 + public final static Property Total = new Property(4, long.class, "total", false, "TOTAL");
  32 + public final static Property Progress = new Property(5, long.class, "progress", false, "PROGRESS");
  33 + public final static Property Title = new Property(6, String.class, "title", false, "TITLE");
  34 + }
  35 +
  36 +
  37 + public DownloadInfoDao(DaoConfig config) {
  38 + super(config);
  39 + }
  40 +
  41 + public DownloadInfoDao(DaoConfig config, DaoSession daoSession) {
  42 + super(config, daoSession);
  43 + }
  44 +
  45 + /** Creates the underlying database table. */
  46 + public static void createTable(Database db, boolean ifNotExists) {
  47 + String constraint = ifNotExists? "IF NOT EXISTS ": "";
  48 + db.execSQL("CREATE TABLE " + constraint + "\"DOWNLOAD_INFO\" (" + //
  49 + "\"_id\" INTEGER PRIMARY KEY NOT NULL ," + // 0: id
  50 + "\"URL\" TEXT UNIQUE ," + // 1: url
  51 + "\"FILE_NAME\" TEXT," + // 2: fileName
  52 + "\"DOWNLOAD_STATUS\" TEXT," + // 3: downloadStatus
  53 + "\"TOTAL\" INTEGER NOT NULL ," + // 4: total
  54 + "\"PROGRESS\" INTEGER NOT NULL ," + // 5: progress
  55 + "\"TITLE\" TEXT);"); // 6: title
  56 + }
  57 +
  58 + /** Drops the underlying database table. */
  59 + public static void dropTable(Database db, boolean ifExists) {
  60 + String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"DOWNLOAD_INFO\"";
  61 + db.execSQL(sql);
  62 + }
  63 +
  64 + @Override
  65 + protected final void bindValues(DatabaseStatement stmt, DownloadInfo entity) {
  66 + stmt.clearBindings();
  67 + stmt.bindLong(1, entity.getId());
  68 +
  69 + String url = entity.getUrl();
  70 + if (url != null) {
  71 + stmt.bindString(2, url);
  72 + }
  73 +
  74 + String fileName = entity.getFileName();
  75 + if (fileName != null) {
  76 + stmt.bindString(3, fileName);
  77 + }
  78 +
  79 + String downloadStatus = entity.getDownloadStatus();
  80 + if (downloadStatus != null) {
  81 + stmt.bindString(4, downloadStatus);
  82 + }
  83 + stmt.bindLong(5, entity.getTotal());
  84 + stmt.bindLong(6, entity.getProgress());
  85 +
  86 + String title = entity.getTitle();
  87 + if (title != null) {
  88 + stmt.bindString(7, title);
  89 + }
  90 + }
  91 +
  92 + @Override
  93 + protected final void bindValues(SQLiteStatement stmt, DownloadInfo entity) {
  94 + stmt.clearBindings();
  95 + stmt.bindLong(1, entity.getId());
  96 +
  97 + String url = entity.getUrl();
  98 + if (url != null) {
  99 + stmt.bindString(2, url);
  100 + }
  101 +
  102 + String fileName = entity.getFileName();
  103 + if (fileName != null) {
  104 + stmt.bindString(3, fileName);
  105 + }
  106 +
  107 + String downloadStatus = entity.getDownloadStatus();
  108 + if (downloadStatus != null) {
  109 + stmt.bindString(4, downloadStatus);
  110 + }
  111 + stmt.bindLong(5, entity.getTotal());
  112 + stmt.bindLong(6, entity.getProgress());
  113 +
  114 + String title = entity.getTitle();
  115 + if (title != null) {
  116 + stmt.bindString(7, title);
  117 + }
  118 + }
  119 +
  120 + @Override
  121 + public Long readKey(Cursor cursor, int offset) {
  122 + return cursor.getLong(offset + 0);
  123 + }
  124 +
  125 + @Override
  126 + public DownloadInfo readEntity(Cursor cursor, int offset) {
  127 + DownloadInfo entity = new DownloadInfo( //
  128 + cursor.getLong(offset + 0), // id
  129 + cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1), // url
  130 + cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // fileName
  131 + cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3), // downloadStatus
  132 + cursor.getLong(offset + 4), // total
  133 + cursor.getLong(offset + 5), // progress
  134 + cursor.isNull(offset + 6) ? null : cursor.getString(offset + 6) // title
  135 + );
  136 + return entity;
  137 + }
  138 +
  139 + @Override
  140 + public void readEntity(Cursor cursor, DownloadInfo entity, int offset) {
  141 + entity.setId(cursor.getLong(offset + 0));
  142 + entity.setUrl(cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1));
  143 + entity.setFileName(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2));
  144 + entity.setDownloadStatus(cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3));
  145 + entity.setTotal(cursor.getLong(offset + 4));
  146 + entity.setProgress(cursor.getLong(offset + 5));
  147 + entity.setTitle(cursor.isNull(offset + 6) ? null : cursor.getString(offset + 6));
  148 + }
  149 +
  150 + @Override
  151 + protected final Long updateKeyAfterInsert(DownloadInfo entity, long rowId) {
  152 + entity.setId(rowId);
  153 + return rowId;
  154 + }
  155 +
  156 + @Override
  157 + public Long getKey(DownloadInfo entity) {
  158 + if(entity != null) {
  159 + return entity.getId();
  160 + } else {
  161 + return null;
  162 + }
  163 + }
  164 +
  165 + @Override
  166 + public boolean hasKey(DownloadInfo entity) {
  167 + throw new UnsupportedOperationException("Unsupported for entities with a non-null key");
  168 + }
  169 +
  170 + @Override
  171 + protected final boolean isEntityUpdateable() {
  172 + return true;
  173 + }
  174 +
  175 +}
app/src/main/java/com/cnlive/downloadfile/download/limit/DownLoadService.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/downloadfile/download/limit/DownLoadService.java
  1 +package com.cnlive.downloadfile.download.limit;
  2 +
  3 +import android.annotation.SuppressLint;
  4 +import android.app.Service;
  5 +import android.content.BroadcastReceiver;
  6 +import android.content.Context;
  7 +import android.content.Intent;
  8 +import android.content.IntentFilter;
  9 +import android.net.ConnectivityManager;
  10 +import android.net.NetworkInfo;
  11 +import android.os.Handler;
  12 +import android.os.IBinder;
  13 +import android.os.Message;
  14 +
  15 +/**
  16 + * 下载服务
  17 + */
  18 +public class DownLoadService extends Service {
  19 + private NetConnectionReceiver netConnectionReceiver;
  20 + private DownloadInfo info = null;
  21 + public static final String ACTION_START = "ACTION_START";
  22 + public static final String ACTION_WAITING = "ACTION_WAITING";
  23 + public static final String ACTION_PAUSE = "ACTION_PAUSE";
  24 + public static final String ACTION_ALLPAUSE = "ACTION_ALLPAUSE";
  25 + public static final String ACTION_ALLSTART = "ACTION_ALLSTRAT";
  26 + public static final String ACTION_DELECT = "ACTION_DELECT";
  27 + public static final String ACION_CLEAR = "ACION_CLEAR";
  28 +
  29 + @Override
  30 + public int onStartCommand(Intent intent, int flags, int startId) {
  31 + //判断Intent的值,获得Activity传来的参数
  32 + if(intent != null){
  33 + info = (DownloadInfo) intent.getSerializableExtra("fileUrl");
  34 + }else {
  35 + return super.onStartCommand(intent, flags, startId);
  36 + }
  37 + if (intent != null && intent.getAction().equals(ACTION_START)) {
  38 + //启动初始化线程
  39 + new MyThread(info).start();
  40 + }else if(intent != null && intent.getAction().equals(ACTION_PAUSE)){
  41 + //暂停
  42 + mHandle.obtainMessage(1, info).sendToTarget();
  43 + }else if(intent != null && intent.getAction().equals(ACTION_ALLPAUSE)){
  44 + //全部暂停
  45 + mHandle.obtainMessage(2).sendToTarget();
  46 + }else if(intent != null && intent.getAction().equals(ACTION_ALLSTART)){
  47 + //全部开始
  48 + mHandle.obtainMessage(3).sendToTarget();
  49 + }else if(intent != null && intent.getAction().equals(ACTION_DELECT)){
  50 + //取消
  51 + mHandle.obtainMessage(4, info).sendToTarget();
  52 + }else if(intent != null && intent.getAction().equals(ACION_CLEAR)){
  53 + //清空
  54 + mHandle.obtainMessage(5).sendToTarget();
  55 + }else if(intent != null && intent.getAction().equals(ACTION_WAITING)){
  56 + mHandle.obtainMessage(6,info).sendToTarget();
  57 + }
  58 + return super.onStartCommand(intent, flags, startId);
  59 + }
  60 +
  61 + @Override
  62 + public void onCreate() {
  63 + super.onCreate();
  64 + IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
  65 + netConnectionReceiver = new NetConnectionReceiver();
  66 + registerReceiver(netConnectionReceiver, filter);
  67 + }
  68 +
  69 + @Override
  70 + public IBinder onBind(Intent intent) {
  71 + return null;
  72 + }
  73 +
  74 + @Override
  75 + public void onDestroy() {
  76 + super.onDestroy();
  77 + unregisterReceiver(netConnectionReceiver);
  78 + }
  79 +
  80 + //用于Service和MyThread之间的通信
  81 + @SuppressLint("HandlerLeak")
  82 + Handler mHandle = new Handler() {
  83 + @Override
  84 + public void handleMessage(Message msg) {
  85 + DownloadInfo info = (DownloadInfo) msg.obj;
  86 + switch (msg.what) {
  87 + case 0:
  88 + //启动一个下载任务
  89 + DownloadLimitManager.getInstance().download(info);
  90 + break;
  91 + case 1://暂停一个
  92 + DownloadLimitManager.getInstance().pauseDownload(info,false);
  93 + break;
  94 + case 2://全部暂停
  95 + DownloadLimitManager.getInstance().pauseAllDownload();
  96 + break;
  97 + case 3:
  98 + DownloadLimitManager.getInstance().startAllDownload();
  99 + break;
  100 + case 4:
  101 + DownloadLimitManager.getInstance().cancelDownload(info,true);
  102 + break;
  103 + case 5:
  104 + DownloadLimitManager.getInstance().clearDownload();
  105 + break;
  106 + case 6:
  107 + DownloadLimitManager.getInstance().pauseToWaitDownload(info);
  108 + break;
  109 + }
  110 + }
  111 + };
  112 +
  113 +
  114 + //子线程 获取文件长度
  115 + class MyThread extends Thread {
  116 + private DownloadInfo fileInfo = null;
  117 + public MyThread(DownloadInfo fileInfo) {
  118 + this.fileInfo = fileInfo;
  119 + }
  120 + public void run() {
  121 + mHandle.obtainMessage(0, fileInfo).sendToTarget();
  122 + }
  123 + }
  124 +
  125 + private class NetConnectionReceiver extends BroadcastReceiver {
  126 +
  127 + @Override
  128 + public void onReceive(Context context, Intent intent) {
  129 + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
  130 + NetworkInfo mobNetInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
  131 + NetworkInfo wifiNetInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
  132 +
  133 + if (!mobNetInfo.isConnected() && !wifiNetInfo.isConnected()) {
  134 + //网络断开
  135 + if(DownloadLimitManager.getInstance().getCurremtDownload() !=null && DownloadLimitManager.getInstance().getCurremtDownload().getUrl() != null){
  136 + DownloadLimitManager.getInstance().pauseErrorDownload();
  137 + }
  138 + } else {
  139 + //网络连接
  140 + //启动初始化线程
  141 + if(DownloadLimitManager.getInstance().getCurremtDownload() !=null && DownloadLimitManager.getInstance().getCurremtDownload().getUrl() != null){
  142 + new MyThread(DownloadLimitManager.getInstance().getCurremtDownload()).start();
  143 + }
  144 + }
  145 + }
  146 + }
  147 +}
0 \ No newline at end of file 148 \ No newline at end of file
app/src/main/java/com/cnlive/downloadfile/download/limit/DownloadIO.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/downloadfile/download/limit/DownloadIO.java
  1 +package com.cnlive.downloadfile.download.limit;
  2 +
  3 +import java.io.Closeable;
  4 +import java.io.IOException;
  5 +
  6 +public class DownloadIO {
  7 + public static void closeAll(Closeable... closeables){
  8 + if(closeables == null){
  9 + return;
  10 + }
  11 + for (Closeable closeable : closeables) {
  12 + if(closeable!=null){
  13 + try {
  14 + closeable.close();
  15 + } catch (IOException e) {
  16 + e.printStackTrace();
  17 + }
  18 + }
  19 + }
  20 + }
  21 +}
app/src/main/java/com/cnlive/downloadfile/download/limit/DownloadInfo.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/downloadfile/download/limit/DownloadInfo.java
  1 +package com.cnlive.downloadfile.download.limit;
  2 +
  3 +
  4 +
  5 +import org.greenrobot.greendao.annotation.Entity;
  6 +
  7 +import org.greenrobot.greendao.annotation.Generated;
  8 +import org.greenrobot.greendao.annotation.Id;
  9 +import org.greenrobot.greendao.annotation.Unique;
  10 +
  11 +import java.io.Serializable;
  12 +
  13 +@Entity
  14 +public class DownloadInfo implements Serializable {
  15 +
  16 + @Id
  17 + private long id;
  18 + @Unique
  19 + private String url;
  20 + private String fileName;
  21 + private String downloadStatus;
  22 + private long total;
  23 + private long progress;
  24 + private String title;
  25 + static final long serialVersionUID = 42L;
  26 + @Generated(hash = 1825047729)
  27 + public DownloadInfo(long id, String url, String fileName, String downloadStatus,
  28 + long total, long progress, String title) {
  29 + this.id = id;
  30 + this.url = url;
  31 + this.fileName = fileName;
  32 + this.downloadStatus = downloadStatus;
  33 + this.total = total;
  34 + this.progress = progress;
  35 + this.title = title;
  36 + }
  37 + @Generated(hash = 327086747)
  38 + public DownloadInfo() {
  39 + }
  40 + public long getId() {
  41 + return this.id;
  42 + }
  43 + public void setId(long id) {
  44 + this.id = id;
  45 + }
  46 + public String getUrl() {
  47 + return this.url;
  48 + }
  49 + public void setUrl(String url) {
  50 + this.url = url;
  51 + }
  52 + public String getFileName() {
  53 + return this.fileName;
  54 + }
  55 + public void setFileName(String fileName) {
  56 + this.fileName = fileName;
  57 + }
  58 + public String getDownloadStatus() {
  59 + return this.downloadStatus;
  60 + }
  61 + public void setDownloadStatus(String downloadStatus) {
  62 + this.downloadStatus = downloadStatus;
  63 + }
  64 + public long getTotal() {
  65 + return this.total;
  66 + }
  67 + public void setTotal(long total) {
  68 + this.total = total;
  69 + }
  70 + public long getProgress() {
  71 + return this.progress;
  72 + }
  73 + public void setProgress(long progress) {
  74 + this.progress = progress;
  75 + }
  76 + public String getTitle() {
  77 + return this.title;
  78 + }
  79 + public void setTitle(String title) {
  80 + this.title = title;
  81 + }
  82 +
  83 +}
app/src/main/java/com/cnlive/downloadfile/download/limit/DownloadLimitManager.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/downloadfile/download/limit/DownloadLimitManager.java
  1 +package com.cnlive.downloadfile.download.limit;
  2 +
  3 +import com.cnlive.downloadfile.Constant;
  4 +import com.cnlive.downloadfile.MyApplication;
  5 +
  6 +import org.greenrobot.eventbus.EventBus;
  7 +
  8 +import java.io.File;
  9 +import java.io.FileOutputStream;
  10 +import java.io.IOException;
  11 +import java.io.InputStream;
  12 +import java.util.ArrayList;
  13 +import java.util.HashMap;
  14 +import java.util.List;
  15 +import java.util.concurrent.atomic.AtomicReference;
  16 +
  17 +import io.reactivex.Observable;
  18 +import io.reactivex.ObservableEmitter;
  19 +import io.reactivex.ObservableOnSubscribe;
  20 +import io.reactivex.ObservableSource;
  21 +import io.reactivex.android.schedulers.AndroidSchedulers;
  22 +import io.reactivex.functions.Function;
  23 +import io.reactivex.functions.Predicate;
  24 +import io.reactivex.schedulers.Schedulers;
  25 +import okhttp3.Call;
  26 +import okhttp3.OkHttpClient;
  27 +import okhttp3.Request;
  28 +import okhttp3.Response;
  29 +
  30 +/**
  31 + * About: 下载管理(限制最大个数)
  32 + */
  33 +public class DownloadLimitManager {
  34 +
  35 + private static final AtomicReference<DownloadLimitManager> INSTANCE = new AtomicReference<>();
  36 + private OkHttpClient mClient;
  37 + private HashMap<String, Call> downCalls; // 用来存放各个下载的请求
  38 + private List<DownloadInfo> downWait; // 用来存放等待下载的请求
  39 + private DownloadInfo currentDownInfo;
  40 +
  41 + public static DownloadLimitManager getInstance() {
  42 + for (; ; ) {
  43 + DownloadLimitManager current = INSTANCE.get();
  44 + if (current != null) {
  45 + return current;
  46 + }
  47 + current = new DownloadLimitManager();
  48 + if (INSTANCE.compareAndSet(null, current)) {
  49 + return current;
  50 + }
  51 + }
  52 + }
  53 +
  54 + private DownloadLimitManager() {
  55 + downCalls = new HashMap<>();
  56 + mClient = new OkHttpClient.Builder().build();
  57 + downWait = new ArrayList<>();
  58 + }
  59 +
  60 + /**
  61 + * 查看是否在下载任务中
  62 + *
  63 + * @param url
  64 + * @return
  65 + */
  66 + public boolean getDownloadUrl(String url) {
  67 + return downCalls.containsKey(url);
  68 + }
  69 +
  70 + /**
  71 + * 查看是否在等待任务中
  72 + *
  73 + * @param url
  74 + * @return
  75 + */
  76 + public boolean getWaitUrl(String url) {
  77 + for (DownloadInfo item : downWait) {
  78 + if (item.getUrl().equals(url)) {
  79 + return true;
  80 + }
  81 + }
  82 + return false;
  83 + }
  84 +
  85 + /**
  86 + * 开始下载
  87 + *
  88 + * @param downloadInfo 下载请求的网址
  89 + */
  90 + public void download(final DownloadInfo downloadInfo) {
  91 + List<DownloadInfo> list = MyApplication.getInstances().getDaoSession().loadAll(DownloadInfo.class);
  92 + downWait.clear();
  93 + downWait = list;
  94 + if (currentDownInfo != null) pauseDownload(currentDownInfo, true);
  95 + if (downCalls.containsKey(downloadInfo.getUrl())) downCalls.remove(downloadInfo.getUrl());
  96 + Observable.just(downloadInfo.getUrl())
  97 + .filter(new Predicate<String>() { // 过滤 call的map中已经有了,就证明正在下载,则这次不下载
  98 + @Override
  99 + public boolean test(String s) {
  100 + boolean flag = downCalls.containsKey(s);
  101 + if (flag) {
  102 + // 如果已经在下载,并且不是暂停的数据,查找下一个文件进行下载
  103 + downNext();
  104 + return false;
  105 + } else {
  106 + // 判断如果正在下载的个数达到最大限制,存到等下下载列表中
  107 + return true;
  108 + }
  109 + }
  110 + })
  111 + .flatMap(new Function<String, ObservableSource<?>>() { // 生成 DownloadInfo
  112 + @Override
  113 + public ObservableSource<?> apply(String s) {
  114 + return Observable.just(createDownInfo(downloadInfo));
  115 + }
  116 + })
  117 + .map(new Function<Object, DownloadInfo>() { // 如果已经下载,重新命名
  118 + @Override
  119 + public DownloadInfo apply(Object o) {
  120 + return getRealFileName((DownloadInfo) o);
  121 + }
  122 + })
  123 + .flatMap(new Function<DownloadInfo, ObservableSource<DownloadInfo>>() { // 下载
  124 + @Override
  125 + public ObservableSource<DownloadInfo> apply(DownloadInfo downloadInfo) {
  126 + currentDownInfo = downloadInfo;
  127 + return Observable.create(new DownloadSubscribe(downloadInfo));
  128 + }
  129 + })
  130 + .observeOn(AndroidSchedulers.mainThread()) // 在主线程中回调
  131 + .subscribeOn(Schedulers.io()) // 在子线程中执行
  132 + .subscribe(new DownloadLimitObserver()); // 添加观察者,监听下载进度
  133 + }
  134 +
  135 + /**
  136 + * 下载等待下载中的第一条
  137 + */
  138 + public void downNext() {
  139 + for (int i = 0; i < downWait.size(); i++) {
  140 + if (!downCalls.containsKey(downWait.get(i).getUrl()) && !downWait.get(i).getUrl().equals(currentDownInfo)) {
  141 + download(downWait.get(i));
  142 + break;
  143 + }
  144 + }
  145 + }
  146 +
  147 + /**
  148 + * 下载取消或者暂停
  149 + *
  150 + * @param downloadInfo
  151 + * @param isPauseCurrent 点击开始下载,那正在下载的文件要暂停,并且执行点击的item,不进行next
  152 + */
  153 + public void pauseDownload(DownloadInfo downloadInfo, boolean isPauseCurrent) {
  154 + Call call = downCalls.get(downloadInfo.getUrl());
  155 + if (call != null) {
  156 + call.cancel();//取消
  157 + }
  158 + downloadInfo.setDownloadStatus(DownloadStatus.DOWNLOAD_PAUSE);
  159 + MyApplication.getInstances().getDaoSession().update(downloadInfo);
  160 + EventBus.getDefault().post(downloadInfo);//通知页面
  161 + for(int i = 0;i < downWait.size();i++){
  162 + if(downWait.get(i).getUrl().equals(downloadInfo.getUrl())){
  163 + downWait.set(i,downloadInfo);
  164 + break;
  165 + }
  166 + }
  167 + //如果暂停的当前文件,next
  168 + if (currentDownInfo != null && downloadInfo.getUrl().equals(currentDownInfo.getUrl()) && !isPauseCurrent) {
  169 + downNext();
  170 + } else {
  171 + downCalls.put(downloadInfo.getUrl(), null);
  172 + }
  173 + }
  174 +
  175 + /**
  176 + * 出错暂停,突然断网
  177 + */
  178 + public void pauseErrorDownload() {
  179 + if (currentDownInfo != null) {
  180 + Call call = downCalls.get(currentDownInfo);
  181 + if (call != null) {
  182 + call.cancel();//取消
  183 + }
  184 + downCalls.remove(currentDownInfo.getUrl());
  185 + }
  186 + }
  187 +
  188 +
  189 + /**
  190 + * 暂停状态转变为等待状态
  191 + * @param downloadInfo
  192 + */
  193 + public void pauseToWaitDownload(DownloadInfo downloadInfo){
  194 + boolean hasWaiting = false;
  195 + int downIndex = 0;
  196 + for(int i = 0;i < downWait.size();i++){
  197 + if(downWait.get(i).getUrl().equals(downloadInfo.getUrl())){
  198 + downIndex = i;
  199 + if(hasWaiting){
  200 + break;
  201 + }
  202 + }
  203 + if(downWait.get(i).getDownloadStatus().equals(DownloadStatus.DOWNLOAD_WAIT) || downWait.get(i).getDownloadStatus().equals(DownloadStatus.DOWNLOAD)){
  204 + hasWaiting = true;
  205 + if(downIndex != 0) {
  206 + break;
  207 + }
  208 + }
  209 + }
  210 + if(hasWaiting){
  211 + downloadInfo.setDownloadStatus(DownloadStatus.DOWNLOAD_WAIT);
  212 + downCalls.remove(downloadInfo.getUrl());
  213 + EventBus.getDefault().post(downloadInfo);//通知页面
  214 + }else {
  215 + downloadInfo.setDownloadStatus(DownloadStatus.DOWNLOAD);
  216 + download(downloadInfo);
  217 + }
  218 + downWait.set(downIndex,downloadInfo);
  219 + MyApplication.getInstances().getDaoSession().update(downloadInfo);//更新数据库状态
  220 + }
  221 +
  222 + /**
  223 + * 暂停所有的下载
  224 + */
  225 + public void pauseAllDownload() {
  226 + for (int i = 0; i < downWait.size(); i++) {
  227 + Call call = downCalls.get(downWait.get(i).getUrl());
  228 + if (call != null) {
  229 + call.cancel();
  230 + if(currentDownInfo != null && downWait.get(i).getUrl() .equals(currentDownInfo.getUrl())){
  231 + downWait.set(i,currentDownInfo);
  232 + }
  233 + }else {
  234 + downCalls.put(downWait.get(i).getUrl(),null);
  235 + }
  236 + downWait.get(i).setDownloadStatus(DownloadStatus.DOWNLOAD_PAUSE);//改变状态
  237 + MyApplication.getInstances().getDaoSession().update(downWait.get(i));//更新数据库状态
  238 + }
  239 + if(currentDownInfo != null){
  240 + EventBus.getDefault().post(currentDownInfo);//通知页面
  241 + currentDownInfo = null;
  242 + }
  243 + }
  244 +
  245 + /**
  246 + * 全部开始
  247 + */
  248 + public void startAllDownload() {
  249 + downCalls.clear();
  250 + downWait.clear();
  251 + List<DownloadInfo> list = MyApplication.getInstances().getDaoSession().loadAll(DownloadInfo.class);
  252 + downWait = list;
  253 + if (downWait.size() > 0) {
  254 + for (int i = 0; i < downWait.size(); i++) {
  255 + downWait.get(i).setDownloadStatus(DownloadStatus.DOWNLOAD_WAIT);//改变状态
  256 + MyApplication.getInstances().getDaoSession().update(downWait.get(i));//更新数据库状态
  257 +// EventBus.getDefault().post(downWait.get(i));//通知页面
  258 + }
  259 + download(downWait.get(0));
  260 + }
  261 + }
  262 +
  263 +
  264 + /**
  265 + * 全部删除清空
  266 + */
  267 + public void clearDownload() {
  268 + for (int i = 0; i < downWait.size(); i++) {
  269 + Call call = downCalls.get(currentDownInfo);
  270 + if (call != null) {
  271 + call.cancel();//取消
  272 + }
  273 + Constant.deleteFile(downWait.get(i).getUrl().substring(downWait.get(i).getUrl().lastIndexOf("/")));
  274 + }
  275 + currentDownInfo = null;
  276 + downWait.clear();
  277 + downCalls.clear();
  278 + MyApplication.getInstances().getDaoSession().deleteAll(DownloadInfo.class);
  279 + }
  280 +
  281 + /**
  282 + * 取消下载 删除本地文件
  283 + *
  284 + * @param info
  285 + */
  286 + public void cancelDownload(DownloadInfo info, boolean fileDelect) {
  287 + //本地数据操作
  288 + for (int i = 0; i < downWait.size(); i++) {
  289 + if (downWait.get(i).getUrl().equals(info.getUrl())) {
  290 + downWait.remove(i);
  291 + MyApplication.getInstances().getDaoSession().delete(info);
  292 + break;
  293 + }
  294 + }
  295 + downCalls.remove(info);
  296 + info.setDownloadStatus(DownloadStatus.DOWNLOAD_CANCEL);
  297 + EventBus.getDefault().post(info);
  298 + if (fileDelect) Constant.deleteFile(info.getFileName());
  299 +
  300 + Call call = downCalls.get(info.getUrl());
  301 + if (call != null) {
  302 + call.cancel();//取消
  303 + }
  304 + //如果暂停的当前文件,next
  305 + if (currentDownInfo != null && info.getUrl().equals(currentDownInfo.getUrl())) {
  306 + downNext();
  307 + }
  308 + }
  309 +
  310 + /**
  311 + * 得到当前正在下载的任务
  312 + *
  313 + * @return
  314 + */
  315 + public DownloadInfo getCurremtDownload() {
  316 + return currentDownInfo;
  317 + }
  318 +
  319 + /**
  320 + * 创建DownInfo
  321 + *
  322 + * @param downloadInfo 请求网址
  323 + * @return DownInfo
  324 + */
  325 + private DownloadInfo createDownInfo(DownloadInfo downloadInfo) {
  326 + long contentLength = getContentLength(downloadInfo.getUrl());//获得文件大小
  327 + downloadInfo.setTotal(contentLength);
  328 + String fileName = downloadInfo.getUrl().substring(downloadInfo.getUrl().lastIndexOf("/"));
  329 + downloadInfo.setFileName(fileName);
  330 + return downloadInfo;
  331 + }
  332 +
  333 + /**
  334 + * 如果文件已下载重新命名新文件名
  335 + *
  336 + * @param downloadInfo
  337 + * @return
  338 + */
  339 + private DownloadInfo getRealFileName(DownloadInfo downloadInfo) {
  340 + String fileName = downloadInfo.getFileName();
  341 + long downloadLength = 0, contentLength = downloadInfo.getTotal();
  342 + File path = new File(Constant.FILE_PATH);
  343 + if (!path.exists()) {
  344 + path.mkdir();
  345 + }
  346 + File file = new File(Constant.FILE_PATH, fileName);
  347 + if (file.exists()) {
  348 + //找到了文件,代表已经下载过,则获取其长度
  349 + downloadLength = file.length();
  350 + }
  351 + //之前下载过,需要重新来一个文件
  352 + int i = 1;
  353 + while (downloadLength >= contentLength) {
  354 + int dotIndex = fileName.lastIndexOf(".");
  355 + String fileNameOther;
  356 + if (dotIndex == -1) {
  357 + fileNameOther = fileName + "(" + i + ")";
  358 + } else {
  359 + fileNameOther = fileName.substring(0, dotIndex)
  360 + + "(" + i + ")" + fileName.substring(dotIndex);
  361 + }
  362 + File newFile = new File(Constant.FILE_PATH, fileNameOther);
  363 + file = newFile;
  364 + downloadLength = newFile.length();
  365 + i++;
  366 + }
  367 + //设置改变过的文件名/大小
  368 + downloadInfo.setProgress(downloadLength);
  369 + downloadInfo.setFileName(file.getName());
  370 + return downloadInfo;
  371 + }
  372 +
  373 + private class DownloadSubscribe implements ObservableOnSubscribe<DownloadInfo> {
  374 + private DownloadInfo downloadInfo;
  375 +
  376 + public DownloadSubscribe(DownloadInfo downloadInfo) {
  377 + this.downloadInfo = downloadInfo;
  378 + }
  379 +
  380 + @Override
  381 + public void subscribe(ObservableEmitter<DownloadInfo> e) throws Exception {
  382 + String url = downloadInfo.getUrl();
  383 + long downloadLength = downloadInfo.getProgress();//已经下载好的长度
  384 + long contentLength = downloadInfo.getTotal();//文件的总长度
  385 + //初始进度信息
  386 + e.onNext(downloadInfo);
  387 + Request request = new Request.Builder()
  388 + //确定下载的范围,添加此头,则服务器就可以跳过已经下载好的部分
  389 + .addHeader("RANGE", "bytes=" + downloadLength + "-" + contentLength)
  390 + .url(url)
  391 + .build();
  392 + Call call = mClient.newCall(request);
  393 + downCalls.put(url, call);//把这个添加到call里,方便取消
  394 +
  395 + Response response = call.execute();
  396 + File file = new File(Constant.FILE_PATH, downloadInfo.getFileName());
  397 + InputStream is = null;
  398 + FileOutputStream fileOutputStream = null;
  399 + try {
  400 + is = response.body().byteStream();
  401 + fileOutputStream = new FileOutputStream(file, true);
  402 + byte[] buffer = new byte[2048];//缓冲数组2kB
  403 + int len;
  404 + while ((len = is.read(buffer)) != -1) {
  405 + fileOutputStream.write(buffer, 0, len);
  406 + downloadLength += len;
  407 + downloadInfo.setProgress(downloadLength);
  408 + currentDownInfo.setProgress(downloadLength);
  409 + e.onNext(downloadInfo);
  410 + }
  411 + fileOutputStream.flush();
  412 + for (int i = 0; i < downWait.size(); i++) {
  413 + if (downWait.get(i).getUrl().equals(url)) {
  414 + MyApplication.getInstances().getDaoSession().delete(downWait.get(i));
  415 + downWait.remove(i);
  416 + downCalls.remove(url);
  417 + currentDownInfo = null;
  418 + downNext();
  419 + break;
  420 + }
  421 + }
  422 + } finally {
  423 + //关闭IO流
  424 + DownloadIO.closeAll(is, fileOutputStream);
  425 +
  426 + }
  427 + e.onComplete();//完成
  428 + }
  429 + }
  430 +
  431 +
  432 + /**
  433 + * 获取下载长度
  434 + *
  435 + * @param downloadUrl
  436 + * @return
  437 + */
  438 + private long getContentLength(String downloadUrl) {
  439 + Request request = new Request.Builder()
  440 + .url(downloadUrl)
  441 + .build();
  442 + try {
  443 + Response response = mClient.newCall(request).execute();
  444 + if (response != null && response.isSuccessful()) {
  445 + long contentLength = response.body().contentLength();
  446 + response.close();
  447 + return contentLength == 0 ? DownloadStatus.TOTAL_ERROR : contentLength;
  448 + }
  449 + } catch (IOException e) {
  450 + e.printStackTrace();
  451 + }
  452 + return DownloadStatus.TOTAL_ERROR;
  453 + }
  454 +
  455 +}
app/src/main/java/com/cnlive/downloadfile/download/limit/DownloadLimitObserver.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/downloadfile/download/limit/DownloadLimitObserver.java
  1 +package com.cnlive.downloadfile.download.limit;
  2 +
  3 +import org.greenrobot.eventbus.EventBus;
  4 +
  5 +import io.reactivex.Observer;
  6 +import io.reactivex.disposables.Disposable;
  7 +
  8 +/**
  9 + * 观察者
  10 + */
  11 +public class DownloadLimitObserver implements Observer<DownloadInfo> {
  12 +
  13 + public Disposable d;//可以用于取消注册的监听者
  14 + public DownloadInfo downloadInfo;
  15 +
  16 + @Override
  17 + public void onSubscribe(Disposable d) {
  18 + this.d = d;
  19 + }
  20 +
  21 + @Override
  22 + public void onNext(DownloadInfo value) {
  23 + this.downloadInfo = value;
  24 + downloadInfo.setDownloadStatus(DownloadStatus.DOWNLOAD);
  25 + EventBus.getDefault().post(downloadInfo);
  26 + }
  27 +
  28 + @Override
  29 + public void onError(Throwable e) {
  30 +// Log.d("My_Log","onError");
  31 +// if (downloadInfo.getUrl() != null && DownloadLimitManager.getInstance().getDownloadUrl(downloadInfo.getUrl())){
  32 +// DownloadLimitManager.getInstance().pauseDownload(downloadInfo.getUrl());
  33 +// downloadInfo.setDownloadStatus(DownloadStatus.DOWNLOAD_ERROR);
  34 +// EventBus.getDefault().post(downloadInfo);
  35 +// }else{
  36 +// downloadInfo.setDownloadStatus(DownloadStatus.DOWNLOAD_PAUSE);
  37 +// EventBus.getDefault().post(downloadInfo);
  38 +// }
  39 + }
  40 +
  41 + @Override
  42 + public void onComplete() {
  43 + if (downloadInfo != null){
  44 + downloadInfo.setDownloadStatus(DownloadStatus.DOWNLOAD_OVER);
  45 + EventBus.getDefault().post(downloadInfo);
  46 + }
  47 + }
  48 +}
app/src/main/java/com/cnlive/downloadfile/download/limit/DownloadStatus.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/downloadfile/download/limit/DownloadStatus.java
  1 +package com.cnlive.downloadfile.download.limit;
  2 +
  3 +public class DownloadStatus {
  4 + /**
  5 + * 下载状态
  6 + */
  7 + public static final String DOWNLOAD = "download"; // 下载中
  8 + public static final String DOWNLOAD_PAUSE = "pause"; // 下载暂停
  9 + public static final String DOWNLOAD_WAIT = "wait"; // 等待下载
  10 + public static final String DOWNLOAD_CANCEL = "cancel"; // 下载取消
  11 + public static final String DOWNLOAD_OVER = "over"; // 下载结束
  12 + public static final String DOWNLOAD_ERROR = "error"; // 下载出错
  13 +
  14 + public static final long TOTAL_ERROR = -1;//获取进度失败
  15 +}
app/src/main/java/com/cnlive/downloadfile/download/util/CheckNetworkStatusChangeListener.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/downloadfile/download/util/CheckNetworkStatusChangeListener.java
  1 +package com.cnlive.downloadfile.download.util;
  2 +
  3 +public interface CheckNetworkStatusChangeListener {
  4 +
  5 + void onEvent(Status status);
  6 +
  7 + /**
  8 + * 网络状态
  9 + * TYPE_UN_NETWORK 沒有网络
  10 + * TYPE_WIFI WiFi连接
  11 + * TYPE_MOBILE 移动数据
  12 + */
  13 + enum Status {
  14 + TYPE_UN_NETWORK,
  15 + TYPE_WIFI,
  16 + TYPE_MOBILE,
  17 + }
  18 +}
app/src/main/java/com/cnlive/downloadfile/download/util/NetWorkUtil.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/downloadfile/download/util/NetWorkUtil.java
  1 +package com.cnlive.downloadfile.download.util;
  2 +
  3 +
  4 +import android.content.Context;
  5 +import android.net.ConnectivityManager;
  6 +import android.net.NetworkInfo;
  7 +
  8 +public class NetWorkUtil {
  9 + /**
  10 + * 获取当前网络类型 CheckNetworkStatusChangeListener.Status
  11 + *
  12 + * @return 返回网络类型 CheckNetworkStatusChangeListener.Status
  13 + */
  14 + public static CheckNetworkStatusChangeListener.Status getNetworkConnectionType(Context context) {
  15 + //获取连接管理器
  16 + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
  17 + if (connectivityManager == null)
  18 + return CheckNetworkStatusChangeListener.Status.TYPE_UN_NETWORK;
  19 + //获取网络连接信息
  20 + NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
  21 + if (networkInfo != null && networkInfo.isAvailable()) {
  22 + if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
  23 + return CheckNetworkStatusChangeListener.Status.TYPE_WIFI;
  24 + }
  25 + if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
  26 + return CheckNetworkStatusChangeListener.Status.TYPE_MOBILE;
  27 + }
  28 + }
  29 + return CheckNetworkStatusChangeListener.Status.TYPE_UN_NETWORK;
  30 + }
  31 +}
0 \ No newline at end of file 32 \ No newline at end of file
app/src/main/res/drawable-v24/ic_launcher_foreground.xml 0 → 100644
  1 +++ a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
  1 +<vector xmlns:android="http://schemas.android.com/apk/res/android"
  2 + xmlns:aapt="http://schemas.android.com/aapt"
  3 + android:width="108dp"
  4 + android:height="108dp"
  5 + android:viewportHeight="108"
  6 + android:viewportWidth="108">
  7 + <path
  8 + android:fillType="evenOdd"
  9 + android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
  10 + android:strokeColor="#00000000"
  11 + android:strokeWidth="1">
  12 + <aapt:attr name="android:fillColor">
  13 + <gradient
  14 + android:endX="78.5885"
  15 + android:endY="90.9159"
  16 + android:startX="48.7653"
  17 + android:startY="61.0927"
  18 + android:type="linear">
  19 + <item
  20 + android:color="#44000000"
  21 + android:offset="0.0" />
  22 + <item
  23 + android:color="#00000000"
  24 + android:offset="1.0" />
  25 + </gradient>
  26 + </aapt:attr>
  27 + </path>
  28 + <path
  29 + android:fillColor="#FFFFFF"
  30 + android:fillType="nonZero"
  31 + android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
  32 + android:strokeColor="#00000000"
  33 + android:strokeWidth="1" />
  34 +</vector>
app/src/main/res/drawable/ic_launcher_background.xml 0 → 100644
  1 +++ a/app/src/main/res/drawable/ic_launcher_background.xml
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<vector xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:width="108dp"
  4 + android:height="108dp"
  5 + android:viewportHeight="108"
  6 + android:viewportWidth="108">
  7 + <path
  8 + android:fillColor="#26A69A"
  9 + android:pathData="M0,0h108v108h-108z" />
  10 + <path
  11 + android:fillColor="#00000000"
  12 + android:pathData="M9,0L9,108"
  13 + android:strokeColor="#33FFFFFF"
  14 + android:strokeWidth="0.8" />
  15 + <path
  16 + android:fillColor="#00000000"
  17 + android:pathData="M19,0L19,108"
  18 + android:strokeColor="#33FFFFFF"
  19 + android:strokeWidth="0.8" />
  20 + <path
  21 + android:fillColor="#00000000"
  22 + android:pathData="M29,0L29,108"
  23 + android:strokeColor="#33FFFFFF"
  24 + android:strokeWidth="0.8" />
  25 + <path
  26 + android:fillColor="#00000000"
  27 + android:pathData="M39,0L39,108"
  28 + android:strokeColor="#33FFFFFF"
  29 + android:strokeWidth="0.8" />
  30 + <path
  31 + android:fillColor="#00000000"
  32 + android:pathData="M49,0L49,108"
  33 + android:strokeColor="#33FFFFFF"
  34 + android:strokeWidth="0.8" />
  35 + <path
  36 + android:fillColor="#00000000"
  37 + android:pathData="M59,0L59,108"
  38 + android:strokeColor="#33FFFFFF"
  39 + android:strokeWidth="0.8" />
  40 + <path
  41 + android:fillColor="#00000000"
  42 + android:pathData="M69,0L69,108"
  43 + android:strokeColor="#33FFFFFF"
  44 + android:strokeWidth="0.8" />
  45 + <path
  46 + android:fillColor="#00000000"
  47 + android:pathData="M79,0L79,108"
  48 + android:strokeColor="#33FFFFFF"
  49 + android:strokeWidth="0.8" />
  50 + <path
  51 + android:fillColor="#00000000"
  52 + android:pathData="M89,0L89,108"
  53 + android:strokeColor="#33FFFFFF"
  54 + android:strokeWidth="0.8" />
  55 + <path
  56 + android:fillColor="#00000000"
  57 + android:pathData="M99,0L99,108"
  58 + android:strokeColor="#33FFFFFF"
  59 + android:strokeWidth="0.8" />
  60 + <path
  61 + android:fillColor="#00000000"
  62 + android:pathData="M0,9L108,9"
  63 + android:strokeColor="#33FFFFFF"
  64 + android:strokeWidth="0.8" />
  65 + <path
  66 + android:fillColor="#00000000"
  67 + android:pathData="M0,19L108,19"
  68 + android:strokeColor="#33FFFFFF"
  69 + android:strokeWidth="0.8" />
  70 + <path
  71 + android:fillColor="#00000000"
  72 + android:pathData="M0,29L108,29"
  73 + android:strokeColor="#33FFFFFF"
  74 + android:strokeWidth="0.8" />
  75 + <path
  76 + android:fillColor="#00000000"
  77 + android:pathData="M0,39L108,39"
  78 + android:strokeColor="#33FFFFFF"
  79 + android:strokeWidth="0.8" />
  80 + <path
  81 + android:fillColor="#00000000"
  82 + android:pathData="M0,49L108,49"
  83 + android:strokeColor="#33FFFFFF"
  84 + android:strokeWidth="0.8" />
  85 + <path
  86 + android:fillColor="#00000000"
  87 + android:pathData="M0,59L108,59"
  88 + android:strokeColor="#33FFFFFF"
  89 + android:strokeWidth="0.8" />
  90 + <path
  91 + android:fillColor="#00000000"
  92 + android:pathData="M0,69L108,69"
  93 + android:strokeColor="#33FFFFFF"
  94 + android:strokeWidth="0.8" />
  95 + <path
  96 + android:fillColor="#00000000"
  97 + android:pathData="M0,79L108,79"
  98 + android:strokeColor="#33FFFFFF"
  99 + android:strokeWidth="0.8" />
  100 + <path
  101 + android:fillColor="#00000000"
  102 + android:pathData="M0,89L108,89"
  103 + android:strokeColor="#33FFFFFF"
  104 + android:strokeWidth="0.8" />
  105 + <path
  106 + android:fillColor="#00000000"
  107 + android:pathData="M0,99L108,99"
  108 + android:strokeColor="#33FFFFFF"
  109 + android:strokeWidth="0.8" />
  110 + <path
  111 + android:fillColor="#00000000"
  112 + android:pathData="M19,29L89,29"
  113 + android:strokeColor="#33FFFFFF"
  114 + android:strokeWidth="0.8" />
  115 + <path
  116 + android:fillColor="#00000000"
  117 + android:pathData="M19,39L89,39"
  118 + android:strokeColor="#33FFFFFF"
  119 + android:strokeWidth="0.8" />
  120 + <path
  121 + android:fillColor="#00000000"
  122 + android:pathData="M19,49L89,49"
  123 + android:strokeColor="#33FFFFFF"
  124 + android:strokeWidth="0.8" />
  125 + <path
  126 + android:fillColor="#00000000"
  127 + android:pathData="M19,59L89,59"
  128 + android:strokeColor="#33FFFFFF"
  129 + android:strokeWidth="0.8" />
  130 + <path
  131 + android:fillColor="#00000000"
  132 + android:pathData="M19,69L89,69"
  133 + android:strokeColor="#33FFFFFF"
  134 + android:strokeWidth="0.8" />
  135 + <path
  136 + android:fillColor="#00000000"
  137 + android:pathData="M19,79L89,79"
  138 + android:strokeColor="#33FFFFFF"
  139 + android:strokeWidth="0.8" />
  140 + <path
  141 + android:fillColor="#00000000"
  142 + android:pathData="M29,19L29,89"
  143 + android:strokeColor="#33FFFFFF"
  144 + android:strokeWidth="0.8" />
  145 + <path
  146 + android:fillColor="#00000000"
  147 + android:pathData="M39,19L39,89"
  148 + android:strokeColor="#33FFFFFF"
  149 + android:strokeWidth="0.8" />
  150 + <path
  151 + android:fillColor="#00000000"
  152 + android:pathData="M49,19L49,89"
  153 + android:strokeColor="#33FFFFFF"
  154 + android:strokeWidth="0.8" />
  155 + <path
  156 + android:fillColor="#00000000"
  157 + android:pathData="M59,19L59,89"
  158 + android:strokeColor="#33FFFFFF"
  159 + android:strokeWidth="0.8" />
  160 + <path
  161 + android:fillColor="#00000000"
  162 + android:pathData="M69,19L69,89"
  163 + android:strokeColor="#33FFFFFF"
  164 + android:strokeWidth="0.8" />
  165 + <path
  166 + android:fillColor="#00000000"
  167 + android:pathData="M79,19L79,89"
  168 + android:strokeColor="#33FFFFFF"
  169 + android:strokeWidth="0.8" />
  170 +</vector>
app/src/main/res/layout/activity_main.xml 0 → 100644
  1 +++ a/app/src/main/res/layout/activity_main.xml
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3 + xmlns:app="http://schemas.android.com/apk/res-auto"
  4 + xmlns:tools="http://schemas.android.com/tools"
  5 + android:layout_width="match_parent"
  6 + android:layout_height="match_parent"
  7 + android:orientation="vertical"
  8 + tools:context=".MainActivity">
  9 +
  10 + <Button
  11 + android:layout_width="wrap_content"
  12 + android:layout_height="wrap_content"
  13 + android:text="单个下载"
  14 + android:onClick="singleDownload"
  15 + android:layout_marginTop="20dp"
  16 + android:visibility="gone"/>
  17 +
  18 + <Button
  19 + android:layout_width="wrap_content"
  20 + android:layout_height="wrap_content"
  21 + android:text="多文件下载"
  22 + android:onClick="multipleDownload"
  23 + android:layout_marginTop="20dp"
  24 + android:visibility="gone"/>
  25 +
  26 + <Button
  27 + android:layout_width="wrap_content"
  28 + android:layout_height="wrap_content"
  29 + android:text="下载"
  30 + android:onClick="limitDownload"
  31 + android:layout_marginTop="20dp" />
  32 +
  33 + <android.support.v7.widget.RecyclerView
  34 + android:id="@+id/file_recycle"
  35 + android:layout_width="match_parent"
  36 + android:layout_height="match_parent"
  37 + android:visibility="gone"
  38 + />
  39 +
  40 + <Button
  41 + android:layout_width="wrap_content"
  42 + android:layout_height="wrap_content"
  43 + android:text="添加1"
  44 + android:onClick="addDownload"
  45 + android:layout_marginTop="20dp"/>
  46 +
  47 + <Button
  48 + android:layout_width="wrap_content"
  49 + android:layout_height="wrap_content"
  50 + android:text="添加2"
  51 + android:onClick="multipleDownload"
  52 + android:layout_marginTop="20dp"/>
  53 +
  54 +</LinearLayout>
0 \ No newline at end of file 55 \ No newline at end of file
app/src/main/res/layout/activity_recycler.xml 0 → 100644
  1 +++ a/app/src/main/res/layout/activity_recycler.xml
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:layout_width="match_parent"
  4 + android:layout_height="match_parent"
  5 + android:orientation="vertical">
  6 +
  7 + <LinearLayout
  8 + android:layout_width="match_parent"
  9 + android:layout_height="wrap_content"
  10 + android:weightSum="2">
  11 +
  12 + <Button
  13 + android:id="@+id/start_pause"
  14 + android:layout_width="wrap_content"
  15 + android:layout_height="wrap_content"
  16 + android:layout_weight="1"
  17 + android:text="全部暂停" />
  18 +
  19 + <Button
  20 + android:id="@+id/clear"
  21 + android:layout_width="wrap_content"
  22 + android:layout_height="wrap_content"
  23 + android:layout_weight="1"
  24 + android:text="一键清空" />
  25 + </LinearLayout>
  26 +
  27 + <android.support.v7.widget.RecyclerView
  28 + android:id="@+id/recycler_view"
  29 + android:layout_width="match_parent"
  30 + android:layout_height="match_parent">
  31 +
  32 + </android.support.v7.widget.RecyclerView>
  33 +
  34 +</LinearLayout>
0 \ No newline at end of file 35 \ No newline at end of file
app/src/main/res/layout/activity_single.xml 0 → 100644
  1 +++ a/app/src/main/res/layout/activity_single.xml
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:layout_width="match_parent"
  4 + android:layout_height="match_parent"
  5 + xmlns:app="http://schemas.android.com/apk/res-auto">
  6 +
  7 + <ProgressBar
  8 + android:id="@+id/main_progress"
  9 + android:layout_width="match_parent"
  10 + android:layout_height="wrap_content"
  11 + app:layout_constraintTop_toTopOf="parent"
  12 + android:layout_marginTop="10dp"
  13 + android:max="10000"
  14 + style="@style/Widget.AppCompat.ProgressBar.Horizontal" />
  15 +
  16 + <Button
  17 + android:id="@+id/main_btn_down"
  18 + android:layout_width="wrap_content"
  19 + android:layout_height="wrap_content"
  20 + android:text="下载"
  21 + app:layout_constraintTop_toBottomOf="@+id/main_progress"
  22 + android:layout_marginTop="10dp"
  23 + app:layout_constraintRight_toLeftOf="@+id/main_btn_pause"
  24 + android:layout_marginRight="20dp"/>
  25 +
  26 + <Button
  27 + android:id="@+id/main_btn_pause"
  28 + android:layout_width="wrap_content"
  29 + android:layout_height="wrap_content"
  30 + android:text="暂停"
  31 + android:layout_marginLeft="20dp"
  32 + app:layout_constraintBaseline_toBaselineOf="@+id/main_btn_down"
  33 + app:layout_constraintRight_toLeftOf="@+id/main_btn_cancel"
  34 + android:layout_marginRight="10dp"/>
  35 +
  36 + <Button
  37 + android:id="@+id/main_btn_cancel"
  38 + android:layout_width="wrap_content"
  39 + android:layout_height="wrap_content"
  40 + android:text="取消"
  41 + android:layout_marginLeft="20dp"
  42 + app:layout_constraintBaseline_toBaselineOf="@+id/main_btn_down"
  43 + app:layout_constraintRight_toRightOf="parent"
  44 + android:layout_marginRight="10dp"/>
  45 +
  46 +</android.support.constraint.ConstraintLayout>
0 \ No newline at end of file 47 \ No newline at end of file
app/src/main/res/layout/item_download_layout.xml 0 → 100644
  1 +++ a/app/src/main/res/layout/item_download_layout.xml
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<RelativeLayout
  3 + xmlns:android="http://schemas.android.com/apk/res/android"
  4 + android:layout_width="match_parent"
  5 + android:layout_height="wrap_content">
  6 +
  7 + <LinearLayout
  8 + android:layout_width="match_parent"
  9 + android:layout_height="wrap_content"
  10 + android:gravity="center_vertical"
  11 + android:orientation="vertical">
  12 +
  13 + <ProgressBar
  14 + android:id="@+id/main_progress"
  15 + android:layout_width="match_parent"
  16 + android:layout_height="wrap_content"
  17 + android:layout_marginTop="10dp"
  18 + android:max="10000"
  19 + style="@style/Widget.AppCompat.ProgressBar.Horizontal" />
  20 +
  21 + <LinearLayout
  22 + android:layout_width="wrap_content"
  23 + android:layout_height="match_parent"
  24 + android:orientation="horizontal"
  25 + android:layout_marginTop="10dp"
  26 + android:layout_marginBottom="10dp"
  27 + android:layout_gravity="right">
  28 + <Button
  29 + android:id="@+id/main_btn_down"
  30 + android:layout_width="wrap_content"
  31 + android:layout_height="wrap_content"
  32 + android:text="下载"/>
  33 + <TextView
  34 + android:id="@+id/name"
  35 + android:layout_width="50dp"
  36 + android:layout_height="wrap_content"
  37 + android:layout_gravity="center"
  38 + android:layout_marginLeft="20dp"/>
  39 +
  40 + <Button
  41 + android:id="@+id/main_btn_cancel"
  42 + android:layout_width="wrap_content"
  43 + android:layout_height="wrap_content"
  44 + android:text="取消"
  45 + android:layout_marginLeft="20dp"/>
  46 +
  47 + </LinearLayout>
  48 +
  49 +
  50 +
  51 + </LinearLayout>
  52 +
  53 +</RelativeLayout>
0 \ No newline at end of file 54 \ No newline at end of file
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml 0 → 100644
  1 +++ a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
  3 + <background android:drawable="@drawable/ic_launcher_background" />
  4 + <foreground android:drawable="@drawable/ic_launcher_foreground" />
  5 +</adaptive-icon>
0 \ No newline at end of file 6 \ No newline at end of file
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml 0 → 100644
  1 +++ a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
  3 + <background android:drawable="@drawable/ic_launcher_background" />
  4 + <foreground android:drawable="@drawable/ic_launcher_foreground" />
  5 +</adaptive-icon>
0 \ No newline at end of file 6 \ No newline at end of file
app/src/main/res/mipmap-hdpi/ic_launcher.png 0 → 100644

2.98 KB

app/src/main/res/mipmap-hdpi/ic_launcher_round.png 0 → 100644

4.91 KB

app/src/main/res/mipmap-mdpi/ic_launcher.png 0 → 100644

2.05 KB

app/src/main/res/mipmap-mdpi/ic_launcher_round.png 0 → 100644

2.79 KB

app/src/main/res/mipmap-xhdpi/ic_launcher.png 0 → 100644

4.46 KB

app/src/main/res/mipmap-xhdpi/ic_launcher_round.png 0 → 100644

6.93 KB

app/src/main/res/mipmap-xxhdpi/ic_launcher.png 0 → 100644

6.31 KB

app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png 0 → 100644

10.4 KB

app/src/main/res/mipmap-xxxhdpi/ic_launcher.png 0 → 100644

9.03 KB

app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png 0 → 100644

15.2 KB

app/src/main/res/values/colors.xml 0 → 100644
  1 +++ a/app/src/main/res/values/colors.xml
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<resources>
  3 + <color name="colorPrimary">#3F51B5</color>
  4 + <color name="colorPrimaryDark">#303F9F</color>
  5 + <color name="colorAccent">#FF4081</color>
  6 + <color name="white">#ffffff</color>
  7 +</resources>
app/src/main/res/values/strings.xml 0 → 100644
  1 +++ a/app/src/main/res/values/strings.xml
  1 +<resources>
  2 + <string name="app_name">DownloadFile</string>
  3 +</resources>
app/src/main/res/values/styles.xml 0 → 100644
  1 +++ a/app/src/main/res/values/styles.xml
  1 +<resources>
  2 +
  3 + <!-- Base application theme. -->
  4 + <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
  5 + <!-- Customize your theme here. -->
  6 + <item name="colorPrimary">@color/colorPrimary</item>
  7 + <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
  8 + <item name="colorAccent">@color/colorAccent</item>
  9 + </style>
  10 +
  11 +</resources>
build.gradle 0 → 100644
  1 +++ a/build.gradle
  1 +// Top-level build file where you can add configuration options common to all sub-projects/modules.
  2 +
  3 +buildscript {
  4 +
  5 + repositories {
  6 + google()
  7 + jcenter()
  8 + }
  9 + dependencies {
  10 + classpath 'com.android.tools.build:gradle:3.3.1'
  11 + classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'
  12 +
  13 + // NOTE: Do not place your application dependencies here; they belong
  14 + // in the individual module build.gradle files
  15 + }
  16 +}
  17 +
  18 +allprojects {
  19 + repositories {
  20 + google()
  21 + jcenter()
  22 + }
  23 +}
  24 +
  25 +task clean(type: Delete) {
  26 + delete rootProject.buildDir
  27 +}
gradle.properties 0 → 100644
  1 +++ a/gradle.properties
  1 +# Project-wide Gradle settings.
  2 +# IDE (e.g. Android Studio) users:
  3 +# Gradle settings configured through the IDE *will override*
  4 +# any settings specified in this file.
  5 +# For more details on how to configure your build environment visit
  6 +# http://www.gradle.org/docs/current/userguide/build_environment.html
  7 +# Specifies the JVM arguments used for the daemon process.
  8 +# The setting is particularly useful for tweaking memory settings.
  9 +org.gradle.jvmargs=-Xmx1536m
  10 +# When configured, Gradle will run in incubating parallel mode.
  11 +# This option should only be used with decoupled projects. More details, visit
  12 +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
  13 +# org.gradle.parallel=true
gradle/wrapper/gradle-wrapper.jar 0 → 100644
No preview for this file type
gradle/wrapper/gradle-wrapper.properties 0 → 100644
  1 +++ a/gradle/wrapper/gradle-wrapper.properties
  1 +#Fri Apr 19 09:44:35 CST 2019
  2 +distributionBase=GRADLE_USER_HOME
  3 +distributionPath=wrapper/dists
  4 +zipStoreBase=GRADLE_USER_HOME
  5 +zipStorePath=wrapper/dists
  6 +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
gradlew 0 → 100644
  1 +++ a/gradlew
  1 +#!/usr/bin/env sh
  2 +
  3 +##############################################################################
  4 +##
  5 +## Gradle start up script for UN*X
  6 +##
  7 +##############################################################################
  8 +
  9 +# Attempt to set APP_HOME
  10 +# Resolve links: $0 may be a link
  11 +PRG="$0"
  12 +# Need this for relative symlinks.
  13 +while [ -h "$PRG" ] ; do
  14 + ls=`ls -ld "$PRG"`
  15 + link=`expr "$ls" : '.*-> \(.*\)$'`
  16 + if expr "$link" : '/.*' > /dev/null; then
  17 + PRG="$link"
  18 + else
  19 + PRG=`dirname "$PRG"`"/$link"
  20 + fi
  21 +done
  22 +SAVED="`pwd`"
  23 +cd "`dirname \"$PRG\"`/" >/dev/null
  24 +APP_HOME="`pwd -P`"
  25 +cd "$SAVED" >/dev/null
  26 +
  27 +APP_NAME="Gradle"
  28 +APP_BASE_NAME=`basename "$0"`
  29 +
  30 +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
  31 +DEFAULT_JVM_OPTS=""
  32 +
  33 +# Use the maximum available, or set MAX_FD != -1 to use that value.
  34 +MAX_FD="maximum"
  35 +
  36 +warn () {
  37 + echo "$*"
  38 +}
  39 +
  40 +die () {
  41 + echo
  42 + echo "$*"
  43 + echo
  44 + exit 1
  45 +}
  46 +
  47 +# OS specific support (must be 'true' or 'false').
  48 +cygwin=false
  49 +msys=false
  50 +darwin=false
  51 +nonstop=false
  52 +case "`uname`" in
  53 + CYGWIN* )
  54 + cygwin=true
  55 + ;;
  56 + Darwin* )
  57 + darwin=true
  58 + ;;
  59 + MINGW* )
  60 + msys=true
  61 + ;;
  62 + NONSTOP* )
  63 + nonstop=true
  64 + ;;
  65 +esac
  66 +
  67 +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
  68 +
  69 +# Determine the Java command to use to start the JVM.
  70 +if [ -n "$JAVA_HOME" ] ; then
  71 + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
  72 + # IBM's JDK on AIX uses strange locations for the executables
  73 + JAVACMD="$JAVA_HOME/jre/sh/java"
  74 + else
  75 + JAVACMD="$JAVA_HOME/bin/java"
  76 + fi
  77 + if [ ! -x "$JAVACMD" ] ; then
  78 + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
  79 +
  80 +Please set the JAVA_HOME variable in your environment to match the
  81 +location of your Java installation."
  82 + fi
  83 +else
  84 + JAVACMD="java"
  85 + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
  86 +
  87 +Please set the JAVA_HOME variable in your environment to match the
  88 +location of your Java installation."
  89 +fi
  90 +
  91 +# Increase the maximum file descriptors if we can.
  92 +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
  93 + MAX_FD_LIMIT=`ulimit -H -n`
  94 + if [ $? -eq 0 ] ; then
  95 + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
  96 + MAX_FD="$MAX_FD_LIMIT"
  97 + fi
  98 + ulimit -n $MAX_FD
  99 + if [ $? -ne 0 ] ; then
  100 + warn "Could not set maximum file descriptor limit: $MAX_FD"
  101 + fi
  102 + else
  103 + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
  104 + fi
  105 +fi
  106 +
  107 +# For Darwin, add options to specify how the application appears in the dock
  108 +if $darwin; then
  109 + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
  110 +fi
  111 +
  112 +# For Cygwin, switch paths to Windows format before running java
  113 +if $cygwin ; then
  114 + APP_HOME=`cygpath --path --mixed "$APP_HOME"`
  115 + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
  116 + JAVACMD=`cygpath --unix "$JAVACMD"`
  117 +
  118 + # We build the pattern for arguments to be converted via cygpath
  119 + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
  120 + SEP=""
  121 + for dir in $ROOTDIRSRAW ; do
  122 + ROOTDIRS="$ROOTDIRS$SEP$dir"
  123 + SEP="|"
  124 + done
  125 + OURCYGPATTERN="(^($ROOTDIRS))"
  126 + # Add a user-defined pattern to the cygpath arguments
  127 + if [ "$GRADLE_CYGPATTERN" != "" ] ; then
  128 + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
  129 + fi
  130 + # Now convert the arguments - kludge to limit ourselves to /bin/sh
  131 + i=0
  132 + for arg in "$@" ; do
  133 + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
  134 + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
  135 +
  136 + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
  137 + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
  138 + else
  139 + eval `echo args$i`="\"$arg\""
  140 + fi
  141 + i=$((i+1))
  142 + done
  143 + case $i in
  144 + (0) set -- ;;
  145 + (1) set -- "$args0" ;;
  146 + (2) set -- "$args0" "$args1" ;;
  147 + (3) set -- "$args0" "$args1" "$args2" ;;
  148 + (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
  149 + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
  150 + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
  151 + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
  152 + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
  153 + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
  154 + esac
  155 +fi
  156 +
  157 +# Escape application args
  158 +save () {
  159 + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
  160 + echo " "
  161 +}
  162 +APP_ARGS=$(save "$@")
  163 +
  164 +# Collect all arguments for the java command, following the shell quoting and substitution rules
  165 +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
  166 +
  167 +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
  168 +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
  169 + cd "$(dirname "$0")"
  170 +fi
  171 +
  172 +exec "$JAVACMD" "$@"
gradlew.bat 0 → 100644
  1 +++ a/gradlew.bat
  1 +@if "%DEBUG%" == "" @echo off
  2 +@rem ##########################################################################
  3 +@rem
  4 +@rem Gradle startup script for Windows
  5 +@rem
  6 +@rem ##########################################################################
  7 +
  8 +@rem Set local scope for the variables with windows NT shell
  9 +if "%OS%"=="Windows_NT" setlocal
  10 +
  11 +set DIRNAME=%~dp0
  12 +if "%DIRNAME%" == "" set DIRNAME=.
  13 +set APP_BASE_NAME=%~n0
  14 +set APP_HOME=%DIRNAME%
  15 +
  16 +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
  17 +set DEFAULT_JVM_OPTS=
  18 +
  19 +@rem Find java.exe
  20 +if defined JAVA_HOME goto findJavaFromJavaHome
  21 +
  22 +set JAVA_EXE=java.exe
  23 +%JAVA_EXE% -version >NUL 2>&1
  24 +if "%ERRORLEVEL%" == "0" goto init
  25 +
  26 +echo.
  27 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
  28 +echo.
  29 +echo Please set the JAVA_HOME variable in your environment to match the
  30 +echo location of your Java installation.
  31 +
  32 +goto fail
  33 +
  34 +:findJavaFromJavaHome
  35 +set JAVA_HOME=%JAVA_HOME:"=%
  36 +set JAVA_EXE=%JAVA_HOME%/bin/java.exe
  37 +
  38 +if exist "%JAVA_EXE%" goto init
  39 +
  40 +echo.
  41 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
  42 +echo.
  43 +echo Please set the JAVA_HOME variable in your environment to match the
  44 +echo location of your Java installation.
  45 +
  46 +goto fail
  47 +
  48 +:init
  49 +@rem Get command-line arguments, handling Windows variants
  50 +
  51 +if not "%OS%" == "Windows_NT" goto win9xME_args
  52 +
  53 +:win9xME_args
  54 +@rem Slurp the command line arguments.
  55 +set CMD_LINE_ARGS=
  56 +set _SKIP=2
  57 +
  58 +:win9xME_args_slurp
  59 +if "x%~1" == "x" goto execute
  60 +
  61 +set CMD_LINE_ARGS=%*
  62 +
  63 +:execute
  64 +@rem Setup the command line
  65 +
  66 +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
  67 +
  68 +@rem Execute Gradle
  69 +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
  70 +
  71 +:end
  72 +@rem End local scope for the variables with windows NT shell
  73 +if "%ERRORLEVEL%"=="0" goto mainEnd
  74 +
  75 +:fail
  76 +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
  77 +rem the _cmd.exe /c_ return code!
  78 +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
  79 +exit /b 1
  80 +
  81 +:mainEnd
  82 +if "%OS%"=="Windows_NT" endlocal
  83 +
  84 +:omega
settings.gradle 0 → 100644
  1 +++ a/settings.gradle
  1 +include ':app'