【文件下载】Rxjava+Retrofit+Mvp完成文件下载

时间:2020-12-02 21:14:15

【文件下载】Rxjava+retrofit+MVP完成文件下载

RxJava跟retrofit实现网络请求真的是太方便了,代码走起,实现下载文件、断点续传功能。
1、导入需要的jar包
/* rxjava */
/* retrofit */
/* okhttp */
compile 'com.android.support:appcompat-v7:26.+'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.0'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
compile 'com.squareup.okhttp3:okhttp:3.3.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
compile 'de.greenrobot:eventbus:2.4.0'
compile 'com.daimajia.numberprogressbar:library:1.4@aar'
2、定义Retrofit请求接口
package com.example.a75213.testdownloaddemo.API;

import okhttp3.ResponseBody;
import retrofit2.http.GET;
import retrofit2.http.Streaming;
import retrofit2.http.Url;
import rx.Observable;

/**
* Created by 75213 on 2017/10/31.
*/

public interface RetrofitApiService {

@Streaming
@GET
Observable<ResponseBody> downloadFileWithDynamicUrlSync(@Url String fileUrl);
}

3、实现接口逻辑封装
package com.example.a75213.testdownloaddemo.helper;

import com.example.a75213.testdownloaddemo.API.RetrofitApiService;
import com.example.a75213.testdownloaddemo.network.download.DownloadProgressInterceptor;
import com.example.a75213.testdownloaddemo.network.download.DownloadProgressListener;
import com.example.a75213.testdownloaddemo.network.exception.CustomizeException;
import com.example.a75213.testdownloaddemo.utils.FileUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.ResponseBody;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import rx.Observable;
import rx.Subscriber;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.schedulers.Schedulers;

/**
* Created by 75213 on 2017/10/31.
*/

public class RetrofitManager {
private static final int DEFAULT_TIMEOUT = 15;
private RetrofitApiService downloadRetrofitApiService;
private static RetrofitManager sInstace = null;

public static RetrofitManager builder() {
if (sInstace == null) {
synchronized (RetrofitManager.class) {
sInstace = new RetrofitManager();
}
}
return sInstace;
}

public RetrofitApiService getDownloadService(String url , DownloadProgressListener listener , File file){
DownloadProgressInterceptor interceptor = new DownloadProgressInterceptor(listener , file);
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(interceptor)
.retryOnConnectionFailure(true)
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.client(client)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
downloadRetrofitApiService = retrofit.create(RetrofitApiService.class);
return downloadRetrofitApiService;
}

//下载
public void downApkFile(Subscription subscription, String path , final File file){
Observable observable = downloadRetrofitApiService.
downloadFileWithDynamicUrlSync(path)
.unsubscribeOn(Schedulers.io())
.map(new Func1<ResponseBody, InputStream>() {
@Override
public InputStream call(ResponseBody responseBody) {
return responseBody.byteStream();
}
})
.observeOn(Schedulers.computation())
.doOnNext(new Action1<InputStream>() {
@Override
public void call(InputStream inputStream) {
try {
FileUtils.writeFile(inputStream ,file);
} catch (IOException e) {
e.printStackTrace();
throw new CustomizeException(e.getMessage(), e);
}
}
})
;
toSubscribe(observable, (Subscriber) subscription);
}


public <T> void toSubscribe(Observable<T> o, Subscriber<T> s) {
o.subscribeOn(Schedulers.io()) // 被观察者的实现线程
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) // 观察者的实现线程
.subscribe(s);
}

}
3.1、断点续传实现代码
package com.example.a75213.testdownloaddemo.utils;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.view.View;

import com.example.a75213.testdownloaddemo.network.exception.CustomizeException;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;

public class FileUtils {

/**
* 写入文件
* 断点续传
* @param in
* @param file
*/
public static void writeFile(InputStream in, File file) throws IOException {
RandomAccessFile savedFile = null;
long ltest = 0;
if (!file.getParentFile().exists())
file.getParentFile().mkdirs();
if (file != null && file.exists()){
ltest = file.length();
}
if (in != null){
savedFile = new RandomAccessFile(file , "rw");
savedFile.seek(ltest);
byte[] buffer = new byte[1024 * 128];
int len = -1;
while ((len = in.read(buffer)) != -1) {
savedFile.write(buffer, 0, len);
}
in.close();
}else {
throw new CustomizeException("下载失败" , new Throwable("检查网网咯"));
}
}

/**
* 读取文件长度
*/
public static long readFile(File file){
if (file != null && file.exists()){
return file.length();
}else {
return 0;
}
}



}

3.2、自定义拦截器【获取下载进度】
package com.example.a75213.testdownloaddemo.network.download;

import com.example.a75213.testdownloaddemo.ResponseBody.DownloadProgressResponseBody;
import com.example.a75213.testdownloaddemo.utils.FileUtils;

import java.io.File;
import java.io.IOException;

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

/**
* Interceptor for download
*/
public class DownloadProgressInterceptor implements Interceptor {
private long startpos;
private DownloadProgressListener listener;

public DownloadProgressInterceptor(DownloadProgressListener listener , File file){
this.listener = listener;
startpos = FileUtils.readFile(file);
}


@Override
public Response intercept(Chain chain) throws IOException {
//断点续传
Request request = chain.request().newBuilder().addHeader("Range" , "bytes=" + startpos + "-").build();
Response originalResponse = chain.proceed(request);
return originalResponse.newBuilder()
.body(new DownloadProgressResponseBody(originalResponse.body(), listener))
.build();
}
}

4、M层
package com.example.a75213.testdownloaddemo.M;

import android.content.Context;
import android.util.Log;

import com.example.a75213.testdownloaddemo.API.RetrofitApiService;
import com.example.a75213.testdownloaddemo.contant.comm;
import com.example.a75213.testdownloaddemo.helper.RetrofitManager;
import com.example.a75213.testdownloaddemo.network.download.DownloadProgressListener;
import com.example.a75213.testdownloaddemo.utils.StringUtils;

import java.io.File;

import rx.Subscriber;
import rx.Subscription;

/**
* Created by 75213 on 2017/10/31.
*/

public class HomeModelImpl implements HomeModel{
private RetrofitApiService dRetrofitService;
private RetrofitManager retrofitManager;

public HomeModelImpl(){
retrofitManager = retrofitManager.builder();
}


@Override
public void downFile(String apkPath , DownloadProgressListener listener, final OnResult lister) {
String baseUrl = StringUtils.getHostName(apkPath);
File outputFile = comm.getPathFile(apkPath);
dRetrofitService = retrofitManager.builder().getDownloadService(baseUrl , listener , outputFile);
Subscription subscription = new Subscriber() {
@Override
public void onCompleted() {
lister.onS();
}

@Override
public void onError(Throwable e) {
Log.e("测试错误" , e.getMessage());
lister.onE(e);
}

@Override
public void onNext(Object o) {

}
};
retrofitManager.downApkFile(subscription , apkPath , outputFile);
}
}

5、P层
package com.example.a75213.testdownloaddemo.P;

import android.os.Environment;

import com.example.a75213.testdownloaddemo.M.HomeModel;
import com.example.a75213.testdownloaddemo.M.HomeModelImpl;
import com.example.a75213.testdownloaddemo.contant.comm;
import com.example.a75213.testdownloaddemo.network.download.DownloadProgressListener;
import com.example.a75213.testdownloaddemo.utils.StringUtils;

import java.io.File;

/**
* Created by 75213 on 2017/11/1.
*/

public class HomePresenterImpl implements HomePresenter , HomeModel.OnResult{
private HomeModel model ;
private onPResult lister;
private DownloadProgressListener listener;

public HomePresenterImpl(DownloadProgressListener listener , onPResult lister){
this.lister = lister;
this.listener = listener;
}

//下载
@Override
public void downApkFile(String path) {
model = new HomeModelImpl();
model.downFile(path, listener , this);
}

@Override
public void onS() {
lister.onSuu();
}

@Override
public void onE(Throwable throwable) {
lister.onTh(throwable);
}
}

6、V层
package com.example.a75213.testdownloaddemo.TestDemo;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.daimajia.numberprogressbar.NumberProgressBar;
import com.example.a75213.testdownloaddemo.P.HomePresenter;
import com.example.a75213.testdownloaddemo.P.HomePresenterImpl;
import com.example.a75213.testdownloaddemo.R;
import com.example.a75213.testdownloaddemo.bean.TestBean;
import com.example.a75213.testdownloaddemo.contant.comm;
import com.example.a75213.testdownloaddemo.network.download.DownloadProgressListener;
import com.example.a75213.testdownloaddemo.utils.StringUtils;

import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;

public class ATestActivity extends AppCompatActivity implements DownloadProgressListener ,HomePresenter.onPResult{
/**
*
*/
private HomePresenter homePresenter;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
TestBean bean = msg.getData().getParcelable("data");
activityfilebar.setProgress(bean.getPorBar());
String strF = getResources().getString(R.string.down_content);
String result = String.format(strF , bean.getByteStr() , bean.getContentStr());
activityfilesize.setText(result);
break;
}
}
};

private android.widget.TextView activitydowntitle;
private android.widget.TextView activitydowncontent;
private android.widget.TextView activitydowngo;
private com.daimajia.numberprogressbar.NumberProgressBar activityfilebar;
private android.widget.LinearLayout activitydownshow;
private android.widget.FrameLayout activitydowning;
private TextView activityfilesize;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_atest);
this.activitydowning = (FrameLayout) findViewById(R.id.activity_downing);
this.activitydownshow = (LinearLayout) findViewById(R.id.activity_down_show);
this.activityfilesize = (TextView) findViewById(R.id.activity_file_size);
this.activityfilebar = (NumberProgressBar) findViewById(R.id.activity_file_bar);
homePresenter = new HomePresenterImpl(this ,this);
this.activitydowngo = (TextView) findViewById(R.id.activity_down_go);
this.activitydowncontent = (TextView) findViewById(R.id.activity_down_content);
this.activitydowntitle = (TextView) findViewById(R.id.activity_down_title);

activitydowngo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
downFileApk();
}
});
}

private int progressNext = -1;
@Override
public void update(long bytesRead, long contentLength, boolean done) {
int progress = 0;
if (0 != contentLength){
progress = (int) (((bytesRead * 100) / contentLength)) ;
}
Log.e("数据回调" , "bytesRead : " + StringUtils.getDataSize(bytesRead) + "\n contentLength" + StringUtils.getDataSize(contentLength) + "\n done " + done + "\n 进度条 " + progress);
if (progressNext != progress){
TestBean bean = new TestBean();
bean.setPorBar(progress);
bean.setByteStr(StringUtils.getDataSize(bytesRead));
bean.setContentStr( StringUtils.getDataSize(contentLength));
Bundle bundle = new Bundle();
bundle.putParcelable("data" , bean);
Message msg = new Message();
msg.what = 1;
msg.setData(bundle);
handler.sendMessage(msg);
progressNext = progress;
}else {
return;
}
}

private void downFileApk(){
activitydownshow.setVisibility(View.GONE);
activitydowning.setVisibility(View.VISIBLE);
homePresenter.downApkFile(url);
}

String url = "http://imtt.dd.qq.com/16891/7C7BB50B68B684A36339AF1F615E2848.apk";

@Override
public void onSuu() {
//安装apk
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.fromFile(comm.getPathFile(url)), "application/vnd.android.package-archive");
startActivity(intent);
finish();
}

@Override
public void onTh(Throwable throwable) {

}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK){
moveTaskToBack(true);
return true;
}
return super.onKeyDown(keyCode, event);
}
}