Preface: recently there is a breakpoint download demand, tamping, and then share

  • Retrofit2+RXjava encapsulation based on MVP Mode
  • Retrofit2+RXjava package file download based on MVP mode
  • Retrofit2+RXjava package file upload based on MVP mode
  • Common Problems with Retrofit2+RXjava encapsulation based on MVP Mode (iv)
  • MVP mode Retrofit2+RXjava encapsulation breakpoint Download (5)
  • Retrofit2+RXjava Encapsulation based on MVP Mode data preprocessing (6)
  • 【Android architecture 】 Retrofit2+RXjava encapsulation based on MVP mode

The file download, which was covered in detail in Part 2 and won’t be covered here, is a wrapper around the previous download, starting with ApiServer

   @Streaming
   @GET
   /** * Large files are officially recommended@StreamingTo annotate, otherwise there will be IO exceptions, small files can be ignored without injection */
   Observable<ResponseBody> downloadFile(@Url String fileUrl);
Copy the code

Define the download callback

public interface DownFileCallback {

    void onSuccess(String path);

    void onFail(String msg);

    void onProgress(long totalSize, long downSize);
}
Copy the code

DownLoadManager

public class DownLoadManager {

    private static DownLoadManager loadManager;

    private HashMap<String, FileObserver> hashMap;
    private OkHttpClient client;
    private Retrofit retrofit;
    private ApiServer apiServer;
    private DownFileCallback fileCallback;
   


    public DownLoadManager(a) {
        hashMap = new HashMap<>();
        client = new OkHttpClient.Builder()
                .addNetworkInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {

                        Request request = chain.request();
 
                       Response response = chain.proceed(request);
                        return response.newBuilder().body(new ProgressResponseBody(response.body(),
                                new ProgressResponseBody.ProgressListener() {
                                    @Override
                                    public void onProgress(long totalSize, long downSize) {
                                        if(fileCallback ! =null) {
                                            fileCallback.onProgress(totalSize, downSize);
                                        }
                                    }
                                })).build();
                    }
                }).build();
        retrofit = new Retrofit.Builder().client(client)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl("https://wawa-api.vchangyi.com/").build();
        apiServer = retrofit.create(ApiServer.class);
    }

    public static DownLoadManager getInstance(a) {
        synchronized (Object.class) {
            if (loadManager == null) {
                loadManager = newDownLoadManager(); }}return loadManager;
    }

 /** * Download a single file **@param url
     * @param fileCallback
     */
    public void downFile(final String url, final DownFileCallback fileCallback) {
        // If downloading, pause
        if (isDownLoad(url)) {
            pause(url);
            return;
        }
        this.fileCallback = fileCallback;
        // The path to the stored file
        final String path = getTemporaryName(url);

        FileObserver observer = apiServer.downloadFile(url).map(new Function<ResponseBody, String>() {
            @Override
            public String apply(ResponseBody body) throws Exception {
                File file = FileUtil.saveFile(path, body);
                return file.getPath();
            }
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeWith(new FileObserver<String>() {
                    @Override
                    public void onSuccess(String path) {

                        fileCallback.onSuccess(path);
                        hashMap.remove(url);
                    }

                    @Override
                    public void onError(String msg) { fileCallback.onFail(msg); hashMap.remove(url); }});/ / save
        hashMap.put(url, observer);
    }
 /** * Suspend/cancel the task **@paramUrl Full URL */
    public void pause(String url) {
        if (hashMap.containsKey(url)) {
            FileObserver observer = hashMap.get(url);
            if(observer ! =null) { observer.dispose(); hashMap.remove(url); }}}/** * get the temporary file name **@param url
     * @return* /
    public static String getTemporaryName(String url) {
        String type = "";
        if (url.contains(".")) {
            type = url.substring(url.lastIndexOf("."));
        }
        String dirName = Environment.getExternalStorageDirectory() + "/mvp/";

        File f = new File(dirName);
        // There is no creation
        if(! f.exists()) { f.mkdirs(); }return dirName + System.currentTimeMillis() + type;
    }


    /** * whether to download **@param url
     * @return* /
    public boolean isDownLoad(String url) {
        return hashMap.containsKey(url);
    }


    public abstract class FileObserver<T> extends DisposableObserver<T> {

        @Override
        public void onNext(T t) {
            onSuccess(t);
        }

        @Override
        public void onError(Throwable e) {
            onError(e.getMessage());
        }

        @Override
        public void onComplete(a) {}public abstract void onSuccess(T o);

        public abstract void onError(String msg); }}Copy the code

There are two things to note about breakpoint downloads

  • 1. To be addedRANGERequest header
if(downSize ! =0&& totalSize ! =0) {
                            request = request.newBuilder()
                                    .addHeader("RANGE"."bytes=" + downSize + "-" + totalSize).build();
                        }
Copy the code

DownSize and totalSize, the former is the start length of the download, the latter is the length of the entire file, these need to pause, do their own save.

  • 2. File writing problem, breakpoint download, can not start from the beginning of the file, need to start from the last end, this is used hereRandomAccessFile
  / * * *@param filePath
     * @paramStart Start position *@param body
     */
    public static File saveFile(String filePath, long start, ResponseBody body) {
        InputStream inputStream = null;
        RandomAccessFile raf = null;
        File file = null;
        try {
            file = new File(filePath);

            raf = new RandomAccessFile(filePath, "rw");
            inputStream = body.byteStream();
            byte[] fileReader = new byte[4096];
            // Move to the position
            raf.seek(start);

            while (true) {
                int read = inputStream.read(fileReader);
                if (read == -1) {
                    break;
                }
                raf.write(fileReader, 0, read); }}catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(inputStream ! =null) {
                try {
                    inputStream.close();
                } catch(IOException e) { e.printStackTrace(); }}if(raf ! =null) {
                try {
                    raf.close();
                } catch(IOException e) { e.printStackTrace(); }}}return file;

    }
Copy the code

Create an entity class to store file attributes, not get and set

public class DownModel {

    private String url;
    private String path;
    private String title;
    private String cover;
    private long totalSize;
    private long currentTotalSize;
    private long downSize;
    private boolean isExists;
    private boolean isFinish;
    private boolean isPause;
}
Copy the code
 /** * Download a single file **@param downModel
     * @param* /
    public void downFile(final DownModel downModel, final DownFileCallback fileCallback) {
        if (downModel == null) {
            return;
        }
        // If downloading, pause
        final String url = downModel.getUrl();
        if (isDownLoad(url)) {
            pause(url);
            Log.e("cheng"."pause url=" + url);
            return;
        }
        // Current link
        currentUrl = url;
        // Whether the download is a breakpoint
        if(downModel.getDownSize() ! =0&& downModel.getTotalSize() ! =0) {
            totalSize = downModel.getTotalSize();
            downSize = downModel.getDownSize();
            currentPath = downModel.getPath();
        } else {
            totalSize = 0;
            downSize = 0;
            currentPath = getTemporaryName(url);
            downModel.setPath(currentPath);
        }

        this.fileCallback = fileCallback;

        Log.e("cheng"."currentUrl=" + currentUrl);

        Log.e("cheng"."downSize=" + downSize + ",totalSize=" + totalSize + ",currentPath=" + currentPath);

        FileObserver observer = apiServer.downloadFile(url).map(new Function<ResponseBody, String>() {
            @Override
            public String apply(ResponseBody body) throws Exception {

                if(downModel.getDownSize() ! =0&& downModel.getTotalSize() ! =0) {
                    return FileUtil.saveFile(currentPath, downModel.getDownSize(), body).getPath();
                }
                File file = FileUtil.saveFile(currentPath, body);
                return file.getPath();
            }
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeWith(new FileObserver<String>() {
                    @Override
                    public void onSuccess(String path) {
                        downModel.setFinish(true);
                        downModel.setPath(path);
                        downModel.setExists(true);


                        if(fileCallback ! =null) {
                            fileCallback.onSuccess(path);
                        }
                        hashMap.remove(url);

                        currentUrl = null;
                    }

                    @Override
                    public void onError(String msg) {
                        if(fileCallback ! =null) {
                            fileCallback.onFail(msg);
                        }
                        hashMap.remove(url);
                        currentUrl = null; }});/ / save
        hashMap.put(url, observer);
    }
Copy the code

Note that if the total file size is 50M and the downloaded size is 10M, onProgress returns totalSize minus the downloaded size 10M (40M) and downSize is the downloaded size

 private void down2(a) {
       String url = "http://download.sdk.mob.com/apkbus.apk";
       if (downModel == null) {
           downModel = new DownModel();
           downModel.setUrl(url);
       }
       DownLoadManager.getInstance().downFile(downModel, new DownFileCallback() {
           @Override
           public void onSuccess(String path) {
               showtoast("Download successful, path=" + path);
           }

           @Override
           public void onFail(String msg) {}@Override
           public void onProgress(long totalSize, long downSize) {
               Log.e("cheng"."totalSize =" + totalSize + ",downSize=" + downSize);
               if (downModel.getTotalSize() == 0) {
                   downModel.setTotalSize(totalSize);
               }
               downModel.setCurrentTotalSize(totalSize);

               downModel.setDownSize(downSize + downModel.getTotalSize() - downModel.getCurrentTotalSize());

               runOnUiThread(new Runnable() {
                   @Override
                   public void run(a) {
                       int progress = (int) (downModel.getDownSize() * 100 / downModel.getTotalSize());
                       tvDown.setText(progress + "%"); sbDown.setProgress(progress); }}); }}); }Copy the code

Finally, Github

Your recognition is the motivation for me to keep updating my blog. If it is useful, please give me a thumbs-up. Thank you