Procházet zdrojové kódy

add download files

yanyi před 6 roky
rodič
revize
2bd58ebb17

+ 2 - 0
app/build.gradle

@@ -33,4 +33,6 @@ dependencies {
     implementation project(':okhttplib')
     implementation 'com.yanyi.benyanyi:loglib:1.0.0'
     implementation 'com.yanyi.benyanyi:permissionlib:1.0.1'
+//    implementation 'io.reactivex.rxjava2:rxjava:2.2.2'
+//    implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
 }

+ 8 - 3
app/src/main/AndroidManifest.xml

@@ -1,11 +1,12 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     package="com.mylove.okhttp">
 
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <!--STORAGE-->
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
     <application
         android:name=".AppContext"
@@ -14,8 +15,12 @@
         android:label="@string/app_name"
         android:roundIcon="@mipmap/ic_launcher_round"
         android:supportsRtl="true"
-        android:theme="@style/AppTheme">
+        android:theme="@style/AppTheme"
+        tools:ignore="GoogleAppIndexingWarning">
         <activity android:name=".MainActivity">
+
+        </activity>
+        <activity android:name=".DownloadActivity">
             <intent-filter>
                 <category android:name="android.intent.category.LAUNCHER" />
                 <action android:name="android.intent.action.MAIN" />

+ 5 - 0
app/src/main/java/com/mylove/okhttp/AppContext.java

@@ -1,6 +1,7 @@
 package com.mylove.okhttp;
 
 import android.app.Application;
+import android.content.Context;
 
 import com.mylove.loglib.JLog;
 
@@ -12,9 +13,13 @@ import com.mylove.loglib.JLog;
  */
 
 public class AppContext extends Application {
+
+    public static Context mContext;
+
     @Override
     public void onCreate() {
         super.onCreate();
         JLog.init(true);
+        mContext = this;
     }
 }

+ 134 - 0
app/src/main/java/com/mylove/okhttp/DownloadActivity.java

@@ -0,0 +1,134 @@
+package com.mylove.okhttp;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ProgressBar;
+
+import com.mylove.loglib.JLog;
+import com.mylove.okhttp.download.DownloadInfo;
+import com.mylove.okhttp.download.DownloadManager;
+import com.mylove.okhttp.download.OnDownloadCallBack;
+
+/**
+ * @author BenYanYi
+ * @date 2018/11/29 14:59
+ * @email ben@yanyi.red
+ * @overview
+ */
+public class DownloadActivity extends AppCompatActivity implements View.OnClickListener {
+    private Button downloadBtn1, downloadBtn2, downloadBtn3;
+    private Button cancelBtn1, cancelBtn2, cancelBtn3;
+    private ProgressBar progress1, progress2, progress3;
+    private String url1 = "http://www.yanyi.red/bluetooth/ios.pdf";
+    private String url2 = "http://www.yanyi.red/bluetooth/dectector/dectector.apk";
+    private String url3 = "http://www.yanyi.red/bluetooth/dectector/dfu_pkg1119.zip";
+
+    private Context mContext;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.act_download);
+        mContext = this;
+        downloadBtn1 = bindView(R.id.main_btn_down1);
+        downloadBtn2 = bindView(R.id.main_btn_down2);
+        downloadBtn3 = bindView(R.id.main_btn_down3);
+
+        cancelBtn1 = bindView(R.id.main_btn_cancel1);
+        cancelBtn2 = bindView(R.id.main_btn_cancel2);
+        cancelBtn3 = bindView(R.id.main_btn_cancel3);
+
+        progress1 = bindView(R.id.main_progress1);
+        progress2 = bindView(R.id.main_progress2);
+        progress3 = bindView(R.id.main_progress3);
+
+        downloadBtn1.setOnClickListener(this);
+        downloadBtn2.setOnClickListener(this);
+        downloadBtn3.setOnClickListener(this);
+
+        cancelBtn1.setOnClickListener(this);
+        cancelBtn2.setOnClickListener(this);
+        cancelBtn3.setOnClickListener(this);
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.main_btn_down1:
+                DownloadManager.getInstance(mContext).download(url1, new OnDownloadCallBack() {
+                    @Override
+                    public void onNext(DownloadInfo downloadInfo) {
+                        JLog.v(downloadInfo.getProgress());
+                        progress1.setMax((int) downloadInfo.getTotal());
+                        progress1.setProgress((int) downloadInfo.getProgress());
+                    }
+
+                    @Override
+                    public void onError(Throwable e) {
+
+                    }
+
+                    @Override
+                    public void onComplete() {
+
+                    }
+                });
+                break;
+            case R.id.main_btn_down2:
+                DownloadManager.getInstance(mContext).download(url2, new OnDownloadCallBack() {
+                    @Override
+                    public void onNext(DownloadInfo downloadInfo) {
+                        JLog.v(downloadInfo.getProgress());
+                    }
+
+                    @Override
+                    public void onError(Throwable e) {
+
+                    }
+
+                    @Override
+                    public void onComplete() {
+
+                    }
+                });
+                break;
+            case R.id.main_btn_down3:
+                DownloadManager.getInstance(mContext).download(url3, new OnDownloadCallBack() {
+                    @Override
+                    public void onNext(DownloadInfo downloadInfo) {
+                        JLog.v(downloadInfo.getProgress());
+                    }
+
+                    @Override
+                    public void onError(Throwable e) {
+
+                    }
+
+                    @Override
+                    public void onComplete() {
+
+                    }
+                });
+                break;
+            case R.id.main_btn_cancel1:
+                DownloadManager.getInstance(mContext).cancel(url1);
+                break;
+            case R.id.main_btn_cancel2:
+                DownloadManager.getInstance(mContext).cancel(url2);
+                break;
+            case R.id.main_btn_cancel3:
+                DownloadManager.getInstance(mContext).cancel(url3);
+                break;
+        }
+    }
+
+    private <T extends View> T bindView(@IdRes int id) {
+        View viewById = findViewById(id);
+        return (T) viewById;
+    }
+
+}

+ 2 - 2
app/src/main/java/com/mylove/okhttp/MainActivity.java

@@ -35,7 +35,7 @@ public class MainActivity extends AppCompatActivity {
             @Override
             public void onAllPermissionSuccess() {
 //                init();
-                downloadDFU();
+                downloadAPK();
             }
 
             @Override
@@ -72,7 +72,7 @@ public class MainActivity extends AppCompatActivity {
         });
     }
 
-    private void downloadDFU() {
+    private void downloadAPK() {
         OkHttpInfo.soapDataTopString = "";
         JLog.init(true);
         String url = "http://www.yanyi.red/bluetooth/dectector/dectector.apk";

+ 83 - 0
app/src/main/res/layout/act_download.xml

@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:padding="@dimen/padding10">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <ProgressBar
+            android:id="@+id/main_progress1"
+            style="@style/Widget.AppCompat.ProgressBar.Horizontal"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1" />
+
+        <Button
+            android:id="@+id/main_btn_down1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="下载1" />
+
+        <Button
+            android:id="@+id/main_btn_cancel1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="取消1" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <ProgressBar
+            android:id="@+id/main_progress2"
+            style="@style/Widget.AppCompat.ProgressBar.Horizontal"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1" />
+
+        <Button
+            android:id="@+id/main_btn_down2"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="下载2" />
+
+        <Button
+            android:id="@+id/main_btn_cancel2"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="取消2" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <ProgressBar
+            android:id="@+id/main_progress3"
+            style="@style/Widget.AppCompat.ProgressBar.Horizontal"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1" />
+
+        <Button
+            android:id="@+id/main_btn_down3"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="下载3" />
+
+        <Button
+            android:id="@+id/main_btn_cancel3"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="取消3" />
+    </LinearLayout>
+
+</LinearLayout>

+ 2 - 2
okhttplib/src/main/java/com/mylove/okhttp/FileUtil.java

@@ -12,13 +12,13 @@ import java.io.IOException;
  * @author myLove
  */
 
-class FileUtil {
+public class FileUtil {
     /**
      * @param saveDir
      * @return
      * @throws IOException 判断下载目录是否存在
      */
-    static String isExistDir(String saveDir) throws IOException {
+    public static String isExistDir(String saveDir) throws IOException {
         // 下载位置
         File downloadFile = new File(Environment.getExternalStorageDirectory(), saveDir);
         if (!downloadFile.mkdirs()) {

+ 29 - 0
okhttplib/src/main/java/com/mylove/okhttp/download/DownLoadObserver.java

@@ -0,0 +1,29 @@
+package com.mylove.okhttp.download;
+
+import io.reactivex.Observer;
+import io.reactivex.disposables.Disposable;
+
+/**
+ * @author BenYanYi
+ * @date 2018/11/29 15:17
+ * @email ben@yanyi.red
+ * @overview
+ */
+public abstract class DownLoadObserver implements Observer<DownloadInfo> {
+    protected Disposable d;//可以用于取消注册的监听者
+    protected DownloadInfo downloadInfo;
+    @Override
+    public void onSubscribe(Disposable d) {
+        this.d = d;
+    }
+
+    @Override
+    public void onNext(DownloadInfo downloadInfo) {
+        this.downloadInfo = downloadInfo;
+    }
+
+    @Override
+    public void onError(Throwable e) {
+        e.printStackTrace();
+    }
+}

+ 69 - 0
okhttplib/src/main/java/com/mylove/okhttp/download/DownloadInfo.java

@@ -0,0 +1,69 @@
+package com.mylove.okhttp.download;
+
+import java.io.File;
+
+/**
+ * @author BenYanYi
+ * @date 2018/11/29 15:18
+ * @email ben@yanyi.red
+ * @overview
+ */
+public class DownloadInfo {
+    public static final long TOTAL_ERROR = -1;//获取进度失败
+    private String url;
+    private long total;
+    private long progress;
+    private String fileName;
+    private File file;
+
+    public DownloadInfo(String url) {
+        this.url = url;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public String getFileName() {
+        return fileName;
+    }
+
+    public void setFileName(String fileName) {
+        this.fileName = fileName;
+    }
+
+    public long getTotal() {
+        return total;
+    }
+
+    public void setTotal(long total) {
+        this.total = total;
+    }
+
+    public long getProgress() {
+        return progress;
+    }
+
+    public void setProgress(long progress) {
+        this.progress = progress;
+    }
+
+    public File getFile() {
+        return file;
+    }
+
+    public void setFile(File file) {
+        this.file = file;
+    }
+
+    @Override
+    public String toString() {
+        return "DownloadInfo{" +
+                "url='" + url + '\'' +
+                ", total=" + total +
+                ", progress=" + progress +
+                ", fileName='" + fileName + '\'' +
+                ", file=" + file +
+                '}';
+    }
+}

+ 224 - 0
okhttplib/src/main/java/com/mylove/okhttp/download/DownloadManager.java

@@ -0,0 +1,224 @@
+package com.mylove.okhttp.download;
+
+import android.content.Context;
+
+import com.mylove.okhttp.FileUtil;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import io.reactivex.Observable;
+import io.reactivex.ObservableEmitter;
+import io.reactivex.ObservableOnSubscribe;
+import io.reactivex.ObservableSource;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.functions.Function;
+import io.reactivex.functions.Predicate;
+import io.reactivex.schedulers.Schedulers;
+import okhttp3.Call;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+/**
+ * @author BenYanYi
+ * @date 2018/11/29 15:16
+ * @email ben@yanyi.red
+ * @overview
+ */
+public class DownloadManager {
+    private static final AtomicReference<DownloadManager> INSTANCE = new AtomicReference<>();
+    private HashMap<String, Call> downCalls;//用来存放各个下载的请求
+    private OkHttpClient mClient;//OKHttpClient;
+    private Context mContext;
+
+    //获得一个单例类
+    public static DownloadManager getInstance(Context mContext) {
+        for (; ; ) {
+            DownloadManager current = INSTANCE.get();
+            if (current != null) {
+                return current;
+            }
+            current = new DownloadManager(mContext);
+            if (INSTANCE.compareAndSet(null, current)) {
+                return current;
+            }
+        }
+    }
+
+    private DownloadManager(Context mContext) {
+        downCalls = new HashMap<>();
+        mClient = new OkHttpClient.Builder().build();
+        this.mContext = mContext;
+    }
+
+    /**
+     * 开始下载
+     *
+     * @param url              下载请求的网址
+     * @param downLoadObserver 用来回调的接口
+     */
+    public void download(String url, DownLoadObserver downLoadObserver) {
+        Observable.just(url)
+                .filter(new Predicate<String>() {//call的map已经有了,就证明正在下载,则这次不下载
+                    @Override
+                    public boolean test(String s) throws Exception {
+                        return !downCalls.containsKey(s);
+                    }
+                })
+                .flatMap(new Function<String, ObservableSource<?>>() {
+                    @Override
+                    public ObservableSource<?> apply(String s) throws Exception {
+                        return Observable.just(createDownInfo(s));
+                    }
+                })
+                .map(new Function<Object, DownloadInfo>() {//检测本地文件夹,生成新的文件名
+                    @Override
+                    public DownloadInfo apply(Object o) throws Exception {
+                        return getRealFileName((DownloadInfo) o);
+                    }
+                })
+                .flatMap(new Function<DownloadInfo, ObservableSource<DownloadInfo>>() {//下载
+                    @Override
+                    public ObservableSource<DownloadInfo> apply(DownloadInfo downloadInfo) throws Exception {
+                        return Observable.create(new DownloadSubscribe(downloadInfo));
+                    }
+                })
+                .observeOn(AndroidSchedulers.mainThread())//在主线程回调
+                .subscribeOn(Schedulers.io())//在子线程执行
+                .subscribe(downLoadObserver);//添加观察者
+
+    }
+
+    public void cancel(String url) {
+        Call call = downCalls.get(url);
+        if (call != null) {
+            call.cancel();//取消
+        }
+        downCalls.remove(url);
+    }
+
+    /**
+     * 创建DownInfo
+     *
+     * @param url 请求网址
+     * @return DownInfo
+     */
+    private DownloadInfo createDownInfo(String url) {
+        DownloadInfo downloadInfo = new DownloadInfo(url);
+        long contentLength = getContentLength(url);//获得文件大小
+        downloadInfo.setTotal(contentLength);
+        String fileName = url.substring(url.lastIndexOf("/"));
+        downloadInfo.setFileName(fileName);
+        return downloadInfo;
+    }
+
+    private DownloadInfo getRealFileName(DownloadInfo downloadInfo) throws Exception {
+        String fileName = downloadInfo.getFileName();
+        String savePath = FileUtil.isExistDir(mContext.getPackageName() + "/download");
+        long downloadLength = 0, contentLength = downloadInfo.getTotal();
+        File file = new File(savePath, fileName);
+        if (file.exists()) {
+            //找到了文件,代表已经下载过,则获取其长度
+            downloadLength = file.length();
+        }
+        //之前下载过,需要重新来一个文件
+        int i = 1;
+        while (downloadLength >= contentLength) {
+            int dotIndex = fileName.lastIndexOf(".");
+            String fileNameOther;
+            if (dotIndex == -1) {
+                fileNameOther = fileName + "(" + i + ")";
+            } else {
+                fileNameOther = fileName.substring(0, dotIndex)
+                        + "(" + i + ")" + fileName.substring(dotIndex);
+            }
+            File newFile = new File(savePath, fileNameOther);
+            file = newFile;
+            downloadLength = newFile.length();
+            i++;
+        }
+        //设置改变过的文件名/大小
+        downloadInfo.setProgress(downloadLength);
+        downloadInfo.setFileName(file.getName());
+        downloadInfo.setFile(file);
+        return downloadInfo;
+    }
+
+    private class DownloadSubscribe implements ObservableOnSubscribe<DownloadInfo> {
+        private DownloadInfo downloadInfo;
+
+        public DownloadSubscribe(DownloadInfo downloadInfo) {
+            this.downloadInfo = downloadInfo;
+        }
+
+        @Override
+        public void subscribe(ObservableEmitter<DownloadInfo> e) throws Exception {
+            String url = downloadInfo.getUrl();
+            long downloadLength = downloadInfo.getProgress();//已经下载好的长度
+            long contentLength = downloadInfo.getTotal();//文件的总长度
+            //初始进度信息
+            e.onNext(downloadInfo);
+
+            Request request = new Request.Builder()
+                    //确定下载的范围,添加此头,则服务器就可以跳过已经下载好的部分
+                    .addHeader("RANGE", "bytes=" + downloadLength + "-" + contentLength)
+                    .url(url)
+                    .build();
+            Call call = mClient.newCall(request);
+            downCalls.put(url, call);//把这个添加到call里,方便取消
+            Response response = call.execute();
+            String savePath = FileUtil.isExistDir(mContext.getPackageName() + "/download");
+            File file = new File(savePath, downloadInfo.getFileName());
+            InputStream is = null;
+            FileOutputStream fileOutputStream = null;
+            try {
+                is = response.body().byteStream();
+                fileOutputStream = new FileOutputStream(file, true);
+                byte[] buffer = new byte[2048];//缓冲数组2kB
+                int len;
+                while ((len = is.read(buffer)) != -1) {
+                    fileOutputStream.write(buffer, 0, len);
+                    downloadLength += len;
+                    downloadInfo.setProgress(downloadLength);
+                    e.onNext(downloadInfo);
+                }
+                downloadInfo.setFile(file);
+                fileOutputStream.flush();
+                downCalls.remove(url);
+            } finally {
+                //关闭IO流
+                IOUtil.closeAll(is, fileOutputStream);
+            }
+            e.onComplete();//完成
+        }
+    }
+
+    /**
+     * 获取下载长度
+     *
+     * @param downloadUrl
+     * @return
+     */
+    private long getContentLength(String downloadUrl) {
+        Request request = new Request.Builder()
+                .url(downloadUrl)
+                .build();
+        try {
+            Response response = mClient.newCall(request).execute();
+            if (response != null && response.isSuccessful()) {
+                long contentLength = response.body().contentLength();
+                response.close();
+                return contentLength == 0 ? DownloadInfo.TOTAL_ERROR : contentLength;
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return DownloadInfo.TOTAL_ERROR;
+    }
+
+}

+ 27 - 0
okhttplib/src/main/java/com/mylove/okhttp/download/IOUtil.java

@@ -0,0 +1,27 @@
+package com.mylove.okhttp.download;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * @author BenYanYi
+ * @date 2018/11/29 16:32
+ * @email ben@yanyi.red
+ * @overview
+ */
+public class IOUtil {
+    public static void closeAll(Closeable... closeables){
+        if(closeables == null){
+            return;
+        }
+        for (Closeable closeable : closeables) {
+            if(closeable!=null){
+                try {
+                    closeable.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+}

+ 16 - 0
okhttplib/src/main/java/com/mylove/okhttp/download/OnDownloadCallBack.java

@@ -0,0 +1,16 @@
+package com.mylove.okhttp.download;
+
+/**
+ * @author BenYanYi
+ * @date 2018/11/29 16:29
+ * @email ben@yanyi.red
+ * @overview
+ */
+public interface OnDownloadCallBack {
+
+    void onNext(DownloadInfo downloadInfo);
+
+    void onError(Throwable e);
+
+    void onComplete();
+}