基于MVP模式,设计自己的RxJava+Retrofit2+Okhttp3+Rxlifecycle开发框架

在开发阶段,如果有一个好的开发框架,不仅能提高开发效率,更能减少后期维护的时间。结合自己的实际,封装了一套MVP+RxJava+Retrofit2+Okhttp3+Rxlifecycle+Butterknife的开发框架。架构层:V层只负责视图的操作,P层只负责数据的交互,M层负责逻辑的处理,应该属于完整意义上的MVP代码结构模式。网络层包括:普通的get/post请求,文件上传、文件带进度下载,无网络缓存策略,请求带加载的dialog等。

先直接上代码链接:

GitHub: https://github.com/634930172/JohnDevFrame

点击打开链接

CSDN: https://download.csdn.net/download/a634930172a/10489294

点击打开链接

 

本项添加的依赖如下:

//Network
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
//RxJava
implementation 'io.reactivex:rxandroid:1.2.1'
implementation 'io.reactivex:rxjava:1.3.0'
//RxLifecycle
implementation 'com.trello:rxlifecycle:0.3.0'
implementation 'com.trello:rxlifecycle-components:0.3.0'
//ButterKnife
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor'com.jakewharton:butterknife-compiler:8.8.1'


MVP模式设计

MVP模式大家应该都有了解过,V层想要得到某个数据然后做视图操作,需通过P层向M层发送请求,M层处理后的结果回调给P层,P层再回调给V层,期间的传递过程都是通过接口访问的。

基于MVP模式,设计自己的RxJava+Retrofit2+Okhttp3+Rxlifecycle开发框架_第1张图片

本项目的结构如下所示:

基于MVP模式,设计自己的RxJava+Retrofit2+Okhttp3+Rxlifecycle开发框架_第2张图片

 

下面看看封装MVP的思路吧。

首先是对Base各个基类的封装。BaseAct如下所示:

public abstract class BaseAct<V,extends BasePresenter<V>> extends RxAppCompatActivity  {
    
public Activity mActivity;
    public 
mPresenter;


    
@Override
    
public void setContentView(@LayoutRes int layoutResID) {
        
super.setContentView(layoutResID);
        
ButterKnife.bind(this);
        
mActivity this;
    
}

    
@Override
    
public void setContentView(View view) {
        
super.setContentView(view);
        
ButterKnife.bind(this);
        
mActivity this;
    
}

    
@Override
    
protected void onCreate(Bundle savedInstanceState) {
        
super.onCreate(savedInstanceState);
        
mPresenter = createPresenter();
        
mPresenter.attachView((Vthis);
        
setContentView(getLayoutId());
        
initView();
        
initData();
        
initEvent();

    
}


    
protected abstract int getLayoutId();

    protected abstract 
createPresenter();


    
/**
     * 
初始化View
     */
    
protected void initView() {
    }

    
/**
     * 
初始化数据
     
*/
    
protected void initData() {
    }

    
/**
     * 
初始化事件
     
*/
    
protected void initEvent() {
    }

    
@Override
    
protected void onDestroy() {
        
if (mPresenter != null) {
           
mPresenter.detachView();
        
}
        
super.onDestroy();

    
}


}

其中V为View的泛型,P为Presenter的泛型,RxAppCompatActivity是Rxlifecycle包下的,如下所示。该类继承了AppCompatActivity,增加了bindUntilEvent()和bindtoLifecycle()等方法,配合网络请求绑定生命周期的方法,可以避免内存泄漏。mPresenter通过调用attachView()和detachView()实现手动绑定和解绑,也起到了防止内存泄漏的作用。

public class RxAppCompatActivity extends AppCompatActivity implements ActivityLifecycleProvider {

    private final BehaviorSubject lifecycleSubject = BehaviorSubject.create();

    @Override
    public final Observable lifecycle() {
        return lifecycleSubject.asObservable();
    }

    @Override
    public final <T> Observable.Transformer<T, T> bindUntilEvent(ActivityEvent event) {
        return RxLifecycle.bindUntilActivityEvent(lifecycleSubject, event);
    }

    @Override
    public final <T> Observable.Transformer<T, T> bindToLifecycle() {
        return RxLifecycle.bindActivity(lifecycleSubject);
    } 

BasePresenter如下所示:

public abstract class BasePresenter<V> implements Presenter<V> {

    protected WeakReference<V> mMvpView;

    @Override
    public void attachView(V mvpView) {
        this.mMvpView = new WeakReference<>(mvpView);
    }


    protected V getView() {
        return mMvpView.get();
    }


    @Override
    public void detachView() {
        if (mMvpView != null) {
            mMvpView.clear();
            mMvpView = null;
        }
    }

BasePresenter将BaseAct绑定的View视图包装成弱引用,防止了内存泄漏(三重保障,稳稳的不泄漏)。Presenter是一个提供方法的接口,如下:

public interface Presenter<V> {

    void attachView(V view);

    void detachView();

}

BaseModule如下所示:

public class BaseModule {

    protected BaseAct mActivity;
    protected BaseFrag mFragment;

    public BaseModule(BaseAct act){
        this.mActivity=act;
    }

    public BaseModule(BaseFrag frag){
        this.mFragment=frag;
    }

    protected  <T> void addActSubscribe(Observable<T> observable,Subscriber<T> subscriber ) {
        observable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .compose(mActivity.<T>bindUntilEvent(ActivityEvent.DESTROY))//绑定生命周期,防止内存泄露
                .subscribe(subscriber);
    }

    protected  <T> void addFragSubscribe(Observable<T> observable,Subscriber<T> subscriber ) {
        observable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .compose(mFragment.<T>bindUntilEvent(FragmentEvent.DESTROY))//绑定生命周期,防止内存泄露
                .subscribe(subscriber);
    }


}

 里面将原始的网络请求方法抽了出来,并持有Act或者Frag的对象。只要子类继承了BaseModule,就可以在Module层为所欲为处理自己想要的逻辑了。

下面是具体的MainAct,MainPresenter,MainModelImp,如下所示:

public class MainAct extends BaseAct, MainPresenter> implements MainView {

    @BindView(R.id.bt)
    Button bt;
    @BindView(R.id.bt2)
    Button bt2;
    @BindView(R.id.bt3)
    Button bt3;
    @BindView(R.id.bt4)
    Button bt4;
    @BindView(R.id.bt5)
    Button bt5;
    @BindView(R.id.bt6)
    Button bt6;
    @BindView(R.id.tv)
    TextView tv;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    protected MainPresenter createPresenter() {
        return new MainPresenter(this);
    }


    /**
     * 点击事件 业务请求
     */
    @OnClick({R.id.bt, R.id.bt2, R.id.bt3, R.id.bt4, R.id.bt5, R.id.bt6})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.bt://get请求
                mPresenter.SimpleGet();
                break;
            case R.id.bt2://post请求
                mPresenter.SimplePost();
                break;
            case R.id.bt3://单图上传
                mPresenter.fileUpload();
                break;
            case R.id.bt4://多图上传
                mPresenter.fileUploads();
                break;
            case R.id.bt5://文件带进度下载
                mPresenter.fileDownLoad();
                break;
            case R.id.bt6://无网络取缓存,测试时将网络关闭
                mPresenter.simpleGetCache();
                break;
        }
    }

    //------------------------------业务回调---------------------------

    /**
     * get请求回调
     */
    @Override
    public void simpleGetCallback(String str) {
        tv.setText(String.valueOf("get成功请求返回数据: " + str));
    }


    /**
     * Post请求回调
     */
    @Override
    public void simplePostCallback(JsonObject jsonObject) {
        tv.setText(String.valueOf("post成功请求返回数据: " + jsonObject));
    }


    /**
     * 单图上传回调
     */
    @Override
    public void fileUploadCallback(JsonObject jsonObject) {
        tv.setText(String.valueOf("单图上传成功返回数据: " + jsonObject));
    }


    /**
     * 多图上传回调
     */
    @Override
    public void fileUploadsCallback(JsonObject jsonObject) {
        tv.setText(String.valueOf("多图上传成功返回数据: " + jsonObject));
    }


    /**
     * 文件下载回调
     */
    @Override
    public void fileDownLoadCallback() {
        tv.setText(String.valueOf("文件下载成功"));
    }

    /**
     * 无网络取缓存回调
     */
    @Override
    public void noNetworkCacheCallback(String str) {
        tv.setText(String.valueOf("无网络缓存数据: " + str));
    }


}

 里面只有mPresenter请求和回调成功后视图的操作,感觉还是很清爽的。

 MainPresenter:

public class MainPresenter extends BasePresenter implements MainModel.LoadingCallBack {

    private MainModel mMainModelImp;

    public MainPresenter(BaseAct act) {
        mMainModelImp = new MainModelImp(act);
    }

    //------------------------V层请求-------------------------------

    /**
     * 普通get请求
     */
    public void SimpleGet() {
        mMainModelImp.getSimpleData(this);
    }

    /**
     * 普通post请求
     */
    public void SimplePost() {
        mMainModelImp.getPostData(this);
    }


    /**
     * 单图上传上传
     */
    public void fileUpload() {
        mMainModelImp.fileUpload(this);
    }

    /**
     * 多图片上传
     */
    public void fileUploads() {
        mMainModelImp.fileUploads(this);
    }

    /**
     * 文件带进度下载
     */
    public void fileDownLoad() {
        mMainModelImp.downLoadFile(this);
    }

    /**
     * 无网络取缓存
     */
    public void simpleGetCache() {
        mMainModelImp.getSimpleCacheData(this);
    }

    //---------------------M层回调-----------------------------------

    /**
     *
     */
    @Override
    public void simpleDataCompleted(String data) {
        getView().simpleGetCallback(data);
    }

    @Override
    public void simplePostCompleted(JsonObject jsonObject) {
        getView().simplePostCallback(jsonObject);
    }

    @Override
    public void fileUploadCompleted(JsonObject jsonObject) {
        getView().fileUploadCallback(jsonObject);
    }

    @Override
    public void fileUploadsCompleted(JsonObject jsonObject) {
        getView().fileUploadsCallback(jsonObject);
    }

    @Override
    public void downLoadFileCompleted() {
        getView().fileDownLoadCallback();
    }

    @Override
    public void simpleCacheDataCompleted(String data) {
        getView().noNetworkCacheCallback(data);
    }


}

MainPresenter只负责将V层传过来的命令发送给M层,然后将M层处理的结果回调给V层,其中没有任何的逻辑操作,P层的职责是很明确的。

MainModelImp:

public class MainModelImp extends BaseModule implements MainModel {


    public MainModelImp(BaseAct act) {
        super(act);
    }

    /**
     * Mget请求的方法,带dialog形式请求
     */
    @Override
    public void getSimpleData(final LoadingCallBack callBack) {
        addActSubscribe(HttpClient.getService(AppService.class).simpleGet(), new RxRequestCallBack(mActivity) {
            @Override
            public void onSuccess(HttpResult httpResult) {
                callBack.simpleDataCompleted(httpResult.getData());
            }
        });
    }

    /**
     * Mpost请求数据的方法
     */
    @Override
    public void getPostData(final LoadingCallBack callBack) {

        String GROUP_ID = "298cea3dabeb1545004451982d6c04f6";
        addActSubscribe(HttpClient.getService(AppService.class).simplePost(GROUP_ID), new RxRequestCallBack() {
            @Override
            public void onSuccess(HttpResult httpResult) {
                callBack.simplePostCompleted(httpResult.getData());
            }
        });

    }

    /**
     * post单图上传
     */
    @Override
    public void fileUpload( final LoadingCallBack callBack) {
        Bitmap bitmap = BitmapFactory.decodeResource(mActivity.getResources(), R.mipmap.ic_launcher);
        byte[] bytes = BitmapUtil.bitmapToBytes(bitmap);//拿到数组
        UploadUtil.Builder builder = new UploadUtil.Builder().
                addByte("upload", bytes);//文件上传工具类
        addActSubscribe(HttpClient.getService(AppService.class).uploadPic(builder.build()), new RxRequestCallBack() {
            @Override
            public void onSuccess(HttpResult httpResult) {
                callBack.fileUploadCompleted(httpResult.getData());
            }
        });
    }

    /**
     * post多图上传
     */
    @Override
    public void fileUploads( final LoadingCallBack callBack) {
        Bitmap bitmap = BitmapFactory.decodeResource(mActivity.getResources(), R.mipmap.ic_launcher);
        byte[] bytes = BitmapUtil.bitmapToBytes(bitmap);//拿到数组
        UploadUtil.Builder builder = new UploadUtil.Builder();
        //多张图片
        for (int i = 0; i < 3; i++) {
            builder.addByte("image[]", bytes, i);
        }
        addActSubscribe(HttpClient.getService(AppService.class).uploadPics(builder.build()), new RxRequestCallBack() {
            @Override
            public void onSuccess(HttpResult httpResult) {
                callBack.fileUploadsCompleted(httpResult.getData());
            }
        });
    }

    /**
     * 文件下载
     */
    @Override
    public void downLoadFile(final LoadingCallBack callBack) {
        String fileName = "app.apk";
        File externalFilesDir = ContextUtil.getContext().getExternalFilesDir(null);//外部存储的私有目录,应用删除后此文件也会被删除
        final FileCallBack downLoadCallback = new FileCallBack(externalFilesDir.toString(), fileName) {

            @Override
            public void onSuccess(ResponseBody responseBody) {
                callBack.downLoadFileCompleted();
            }

            @Override
            public void progress(long progress) {
                LogUtil.e("progress: " + progress / 1024 + "kb  total: " + FileLoadEvent.getInstance().getTotal() / 1024 + "kb");
            }

            @Override
            public void onStart() {
                LogUtil.e("onStart");
            }

            @Override
            public void onCompleted() {
                LogUtil.e("onCompleted");
            }

            @Override
            public void onError(Throwable e) {
                if (e instanceof SocketTimeoutException) {
                    LogUtil.e("SocketTimeoutException: 网络中断,请检查您的网络状态");
                } else if (e instanceof ConnectException) {
                    LogUtil.e("ConnectException: 网络中断,请检查您的网络状态");
                } else if (e instanceof UnknownHostException) {
                    LogUtil.e("UnknownHostException: 网络中断,请检查您的网络状态");
                } else {
                    LogUtil.e("onError:其他错误:" + e.getMessage() + "  cause: " + e.getCause());
                }
                e.printStackTrace();
            }
        };
        //重写了ResponseBodyHttpClient
        String URL = "http://download.fir.im/v2/app/install/5818acbcca87a836f50014af?download_token=a01301d7f6f8f4957643c3fcfe5ba6ff";
        DownLoadHttpClient.getService(AppService.class).download(URL)
                .subscribeOn(Schedulers.io())//请求网络 在调度者的io线程
                .observeOn(Schedulers.io()) //指定线程保存文件
                .doOnNext(new Action1() {
                    @Override
                    public void call(ResponseBody body) {
                        downLoadCallback.saveFile(body);
                    }
                })
                .observeOn(AndroidSchedulers.mainThread()) //在主线程中更新ui
                .compose(mActivity.bindUntilEvent(ActivityEvent.DESTROY))
                .subscribe(new FileSubscriber(downLoadCallback));
    }


    /**
     * 无网络取缓存
     */
    @Override
    public void getSimpleCacheData(final LoadingCallBack callBack) {
        addActSubscribe(HttpClient.getService(AppService.class).simpleGetCache(), new RxRequestCallBack() {
            @Override
            public void onSuccess(HttpResult httpResult) {
                callBack.simpleCacheDataCompleted(httpResult.getData());
            }
        });
    }


}

这里是MainModelImp类,上面的这些网络请求方法可以先不管,我们只要知道这些复杂的逻辑都在这里处理,然后通过接口将结果回调给P层就可以了。


网络层

网络层的设计,代码结构如下:

基于MVP模式,设计自己的RxJava+Retrofit2+Okhttp3+Rxlifecycle开发框架_第3张图片

 虽然看起来有那么多类别,但是里面的结构还是很清晰的。我们先来看最关键的RxRequestCallBack类,如下所示:

public abstract class RxRequestCallBack<T> extends SubscriberT>> implements DialogInterface.OnCancelListener {

    private LoadingDialog dialog;
    private static final String TAG="RxRequestCallBack";
    /**
     * 网络请求成功的回调方法,必须重写
     */
    public abstract void onSuccess(HttpResult<T> httpResult);

    /**
     * 默认访问请求
     */
    protected RxRequestCallBack() {

    }

    @Override
    public void onStart() {
        super.onStart();
        if (dialog != null) {
            dialog.show();
            LogUtil.e("dialogShow");
        }
    }

    /**
     * 请求有进度框
     */
    public RxRequestCallBack(Context context) {
        super();
        dialog = new LoadingDialog(context);
        dialog.setOnCancelListener(this);
    }

    /**
     * 请求有进度框
     */
    public RxRequestCallBack(Context context, String loadingMsg) {
        super();
        dialog = new LoadingDialog(context, loadingMsg);
        dialog.setOnCancelListener(this);
    }

    @Override
    public void onCompleted() {
        dismissDialog();
        Log.e(TAG, "onCompleted: " );
    }

    @Override
    public void onError(Throwable e) {
        dismissDialog();
        if (e instanceof SocketTimeoutException) {
            Log.e(TAG,"SocketTimeoutException: 网络中断,请检查您的网络状态");
        } else if (e instanceof ConnectException) {
            Log.e(TAG,"ConnectException: 网络中断,请检查您的网络状态");
        } else if (e instanceof UnknownHostException) {
            Log.e(TAG,"UnknownHostException: 网络中断,请检查您的网络状态");
        } else {
            Log.e(TAG,"onError:其他错误:" + e.getMessage() + "  cause: " + e.getCause());
        }
        e.printStackTrace();
    }

    @Override
    public void onNext(HttpResult<T> tHttpResult) {
        Log.e(TAG,"onNext:   code--" + tHttpResult.getCode() + "--msg--" + tHttpResult.getMsg());
        if (tHttpResult.getCode() == 401) {
            Log.e(TAG,"onNext:  Json_error");
        } else if (tHttpResult.getCode() != 200) {
            onFailed(tHttpResult);
            Log.e(TAG,"onNext:  onFailed");
        } else {
            onSuccess(tHttpResult);
            Log.e(TAG,"onNext:  onSuccess");
        }
    }

    protectedvoid onFailed(HttpResult<T> tHttpResult) {
    }


    private void dismissDialog() {
        //延时消除dialog,为了演示效果  实际项目中可去掉
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                if (dialog != null && dialog.isShowing()) {
                    dialog.dismiss();
                    Log.e(TAG,"dialogDismiss");
                }
            }
        },3000);


    }

    /**
     * dialog消失的时候取消订阅
     */
    @Override
    public void onCancel(DialogInterface dialogInterface) {
        if (!this.isUnsubscribed()) {
            this.unsubscribe();
        }
    }


}

该类继承了Subscriber,Subscriber的结构,点开查看:

public abstract class Subscriber<T> implements Observer<T>, Subscription {

是一个抽象类,它实现了Observer和Subscription,所以它既可以充当Observer的实现类,又可以通过Subscription实现订阅以及取消订阅的功能。HttpResult类将请求结果的最外层做了统一的处理,HttpResult类如下: 

public class HttpResult<T> {
    /**
     * 错误码
     */
    private int code;
    /**
     * 错误信息
     */
    private String msg;
    /**
     * 消息响应的主体
     */
    private T data;
    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }


}

因服务端定义最外层的字段不一样,我们可以自己修改里面的字段,这里例子里面只定义了code、msg还有里面的数据data,由于data不是固定的,所以定义成泛型。这样做的好处就是可以先对最外层统一判断code和msg的值,再处理返回数据后的逻辑。

再回来看RxRequestCallback,继承Subsriber后,会实现Observer的三个方法,onNext()、onError()和onCompleted(),Subsriber还有一个onStart()方法,可以处理开始请求时的一些逻辑操作,我将dialog的显示逻辑放在这了,然后在请求结束后关闭。正常情况下,数据请求成功会走onStart()、onNext()、onCompleted()三个方法,异常会走onStart()、onError()、onCompleted()三个方法。先看定义成功的逻辑:在这里我们定义返回成功是服务端返回的code为200的时候,并将后面的处理逻辑用抽象方法抽出来,让后面具体的请求后具体实现它的逻辑。失败的逻辑:走OnNext()方法但code不是200的情况,需要处理的话重写就好了。

第二个是HttpClient类,用来请求数据的client,应该没有什么可以说的:

public class HttpClient {

    private static final long cacheSize = 1024 * 1024 * 10;// 缓存文件最大限制大小10M
    private static String cacheDirectory = ContextUtil.getContext().//缓存路径
            getExternalCacheDir().getAbsolutePath() + "/OkHttp_Cache";
    private static Cache cache = new Cache(new File(cacheDirectory), cacheSize)//缓存对象

    private Retrofit retrofit;

    private static final int DEFAULT_TIMEOUT = 5;

    //构造方法私有
    private HttpClient() {
        // 创建一个OkHttpClient
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        // 设置网络请求超时时间
        builder.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
        builder.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
        builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
        // 失败后尝试重新请求
        builder.retryOnConnectionFailure(true);
        //----------------------------基本设置------------------------------------------------------
        builder.addInterceptor(new AcheInterceptor());//缓存拦截器
        builder.cache(cache);//缓存设置
        retrofit = new Retrofit.Builder()
                .client(builder.build())
                .addConverterFactory(HttpCovertFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(BaseConfig.BASE_URL)
                .build();
    }

    /**
     * 调用单例对象
     */
    private static HttpClient getInstance() {
        return SingletonHolder.INSTANCE;
    }

    /**
     * 创建单例对象
     */
    private static class SingletonHolder {
        static HttpClient INSTANCE = new HttpClient();
    }

    /**
     * @return 指定service实例
     */
    public static <T> T getService(Class<T> clazz) {
        return HttpClient.getInstance().retrofit.create(clazz);
    }

}

 AppService

通过getService()得到请求的服务类,服务类如下:

public interface AppService {

    //Get请求
    @GET("https://www.mrallen.cn/test.php")
    Observable> simpleGet();

    //Post请求
    @FormUrlEncoded
    @POST("https://www.mrallen.cn/api/allen_restful/rcloud/group/queryGroupMemberInfo.php")
    Observable> simplePost(@Field("group_id") String groupId);

    //单图上传
    @Multipart
    @POST("allen_restful/upload/upload.php")
    Observable> uploadPic(@PartMap Map, RequestBody> map);

    //多图上传
    @Multipart
    @POST("https://www.mrallen.cn/api/allen_restful/upload/testMuchUploadImg.php")
    Observable> uploadPics(@PartMap Map, RequestBody> map);

    //文件下载
    @Streaming
    @GET
    Observable download(@Url String url);

    //无网络取缓存
    @Headers(AcheInterceptor.CACHE)
    @GET("https://www.mrallen.cn/test.php")
    Observable> simpleGetCache();


}


get/post请求

有了Observable和RxRequestCallBack,最基本的请求格式应该是这样的:

接口类:

//Get请求
@GET("https://www.mrallen.cn/test.php")
Observable> simpleGet();

MainModelImp调用代码:

HttpClient.getService(AppService.class).simpleGet().subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .compose(mActivity.>bindUntilEvent(ActivityEvent.DESTROY))//绑定生命周期,防止内存泄露
        .subscribe(new RxRequestCallBack() {
            @Override
            public void onSuccess(HttpResult httpResult) {
                
            }
        });

感觉要调用那么多代码,可以将公共的部分抽出来封装成一个方法,放到BaseModule去,,如下:

protected  <T> void addActSubscribe(Observable<T> observable,Subscriber<T> subscriber ) {
    observable.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .compose(mActivity.<T>bindUntilEvent(ActivityEvent.DESTROY))//绑定生命周期,防止内存泄露
            .subscribe(subscriber);
}

所以在MainModelImp的请求就可以变成这样:

addActSubscribe(HttpClient.getService(AppService.class).simpleGet(), new RxRequestCallBack(mActivity) {
    @Override
    public void onSuccess(HttpResult httpResult) {
        callBack.simpleDataCompleted(httpResult.getData());
    }
});

看起来简洁多了,如果要处理失败逻辑,重写OnFailed()或者OnError() 就可以了。


文件上传

说完了基本的使用,get/post请求应该没有问题了,现在对图片上传功能做封装。

文件的上传,主要在UploadUtil工具类,如下:

public class UploadUtil {

    public static class Builder {

        Map, RequestBody> params;

        public Builder() {
            params = new LinkedHashMap<>();
        }

        public Map, RequestBody> build() {
            return params;
        }

        /**
         * 文件形式上传表单
         */
        public Builder addFile(String key, File file) {
            RequestBody requestBody = RequestBody
                    .create(MultipartBody.FORM, file);
            params.put(key + "\"; filename=\"" + file.getName(), requestBody);
            return this;
        }

        /**
         * 参数上传
         */
        public Builder addString(String key, String value) {
            RequestBody requestBody = RequestBody
                    .create(MultipartBody.FORM, value);
            params.put(key, requestBody);
            return this;
        }

        /**
         * 数组形式上传表单
         */
        public Builder addByte(String key, byte[] bytes) {
            RequestBody requestBody = RequestBody
                    .create(MultipartBody.FORM, bytes);
            long currentTime = System.currentTimeMillis();
            params.put(key + "\"; filename=\"" + currentTime + ".jpg", requestBody);
            return this;
        }


        /**
         * 多图上传
         */
        public Builder addByte(String key, byte[] bytes, int size) {
            RequestBody requestBody = RequestBody
                    .create(MultipartBody.FORM, bytes);
            long currentTime = System.currentTimeMillis();
            params.put(key + "\"; filename=\"" + currentTime + size + ".jpg", requestBody);
            return this;
        }


    }

}

支持以文件、数组的形式上传,本例都是以数组的形式上传的,图片上传的使用示例如下:

接口类:

//单图上传
@Multipart
@POST("allen_restful/upload/upload.php")
Observable> uploadPic(@PartMap Map, RequestBody> map);

调用层,以传一张默认的luncher图片为例:

/**
 * post单图上传
 */
@Override
public void fileUpload( final LoadingCallBack callBack) {
    Bitmap bitmap = BitmapFactory.decodeResource(mActivity.getResources(), R.mipmap.ic_launcher);
    byte[] bytes = BitmapUtil.bitmapToBytes(bitmap);//拿到数组
    UploadUtil.Builder builder = new UploadUtil.Builder().
            addByte("upload", bytes);//文件上传工具类
    addActSubscribe(HttpClient.getService(AppService.class).uploadPic(builder.build()), new RxRequestCallBack() {
        @Override
        public void onSuccess(HttpResult httpResult) {
            callBack.fileUploadCompleted(httpResult.getData());
        }
    });
}

将图片转成数组,UploadUtil工具类确定图片的名称和格式,通过builder.build()方法得到Map对象,这样图片的上传就完成了,多文件上传的原理是一样的。

 

文件带进度下载

关于文件的下载,这里参考了简书里的一篇文章,有兴趣的看官可以看一下,链接如下:

https://www.jianshu.com/p/060d55fc1c82

接口类的定义如下:

//文件下载
@Streaming
@GET
Observable download(@Url String url);

使用@streaming配合@url注解,将文件以流的形式处理, okHttp3默认的ResponseBody因为不知道进度的相关信息,所以需要对其进行改造。可以使用接口监听进度信息。改造的ProgressResponseBody如下:

public class ProgressResponseBody extends ResponseBody {
    private ResponseBody responseBody;
    private BufferedSource bufferedSource;

    public ProgressResponseBody(ResponseBody responseBody) {
        this.responseBody = responseBody;

    }

    @Nullable
    @Override
    public MediaType contentType() {
        return  responseBody.contentType();
    }

    @Override
    public long contentLength() {
        FileLoadEvent.getInstance().setTotalProgress(responseBody.contentLength());
        return responseBody.contentLength();
    }

    @Override
    public BufferedSource source() {
        if (bufferedSource == null) {
            bufferedSource = Okio.buffer(source(responseBody.source()));
        }
        return bufferedSource;

    }

    private Source source(Source source) {
        return new ForwardingSource(source) {
            long bytesReaded = 0;
            @Override
            public long read(@NonNull Buffer sink, long byteCount) throws IOException {
                long bytesRead = super.read(sink, byteCount);
                bytesReaded += bytesRead == -1 ? 0 : bytesRead;
                //实时发送当前已读取的字节和总字节
                RxBus.getInstance().post(FileLoadEvent.getInstance().setProgress(bytesReaded));
                return bytesRead;
            }
        };
    }


}

关键是read()方法,每有读取的字节,都会有回调,然后通过RxBus实时更新给FileLoadEvent。总文件的大小可通过contentLenght()方法获得。

FileLoadEvent如下:

public class FileLoadEvent {

    private static FileLoadEvent INSTANCE = new FileLoadEvent();

    private FileLoadEvent() {
    }

    public static FileLoadEvent getInstance() {
        return INSTANCE;
    }


    private long total;
    private long bytesLoaded;

    long getBytesLoaded() {
        return bytesLoaded;
    }

   public long getTotal() {
        return total;
    }

    void setTotalProgress(long total) {
        this.total = total;
    }

    FileLoadEvent setProgress(long bytesLoaded) {
        this.bytesLoaded = bytesLoaded;
        return this;
    }

}

作用就是记录当前进度和总进度。

定义好重写的ResponseBody后,将它写入Intercepter拦截器中:

public class ProgressInterceptor implements Interceptor {


    @Override
    public Response intercept(@NonNull Chain chain) throws IOException {
        Response originalResponse = chain.proceed(chain.request());
        //新项目下载下来的
        return originalResponse.newBuilder()
                .body(new ProgressResponseBody(originalResponse.body()))
                .build();
    }
}

 为了获得当前进度以及保存下载的文件的操作,我们定义了一个FileCallBack类,如下所示:

public abstract class FileCallBack<T> {


    private String destFileDir;
    private String destFileName;


    public FileCallBack(String destFileDir, String destFileName) {
        this.destFileDir = destFileDir;
        this.destFileName = destFileName;
        subscribeLoadProgress();
    }

    public abstract void onSuccess(T t);

    public abstract void progress(long progress);

    public abstract void onStart();

    public abstract void onCompleted();

    public abstract void onError(Throwable e);

    public void saveFile(ResponseBody body) {
        InputStream is = null;
        byte[] buf = new byte[2048];
        int len;
        FileOutputStream fos = null;
        try {
            is = body.byteStream();
            File dir = new File(destFileDir);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            File file = new File(dir, destFileName);
            fos = new FileOutputStream(file);
            while ((len = is.read(buf)) != -1) {
                fos.write(buf, 0, len);
            }
            fos.flush();
            unsubscribe();
            //onCompleted();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null)
                    is.close();
                if (fos != null)
                    fos.close();
            } catch (IOException e) {
                Log.e("saveFile", e.getMessage());
            }
        }
    }

    /**
     * 订阅加载的进度条
     */
    public void subscribeLoadProgress() {
        Subscription subscription = RxBus.getInstance().doSubscribe(FileLoadEvent.class, new Action1() {
            @Override
            public void call(FileLoadEvent fileLoadEvent) {
                progress(fileLoadEvent.getBytesLoaded());
            }
        }, new Action1() {
            @Override
            public void call(Throwable throwable) {
                //TODO 对异常的处理
            }
        });
        RxBus.getInstance().addSubscription(this, subscription);
    }

    /**
     * 取消订阅,防止内存泄漏
     */
    public void unsubscribe() {
        RxBus.getInstance().unSubscribe(this);
    }

}

然后重写一个FileSubscriber继承Subscriber,如下:

public class FileSubscriber<T> extends Subscriber<T> {

    private FileCallBack fileCallBack;

    public FileSubscriber(FileCallBack fileCallBack) {
        this.fileCallBack = fileCallBack;
    }

    @Override
    public void onStart() {
        super.onStart();
        if (fileCallBack != null)
            fileCallBack.onStart();
    }

    @Override
    public void onCompleted() {
        if (fileCallBack != null)
            fileCallBack.onCompleted();
    }

    @Override
    public void onError(Throwable e) {
        if (fileCallBack != null)
            fileCallBack.onError(e);
    }

    @Override
    public void onNext(T t) {
        if (fileCallBack != null)
            fileCallBack.onSuccess(t);
    }




}

最后,在MainModuelImp的调用如下:

/**
 * 文件下载
 */
@Override
public void downLoadFile(final LoadingCallBack callBack) {
    String fileName = "app.apk";
    File externalFilesDir = ContextUtil.getContext().getExternalFilesDir(null);//外部存储的私有目录,应用删除后此文件也会被删除
    final FileCallBack downLoadCallback = new FileCallBack(externalFilesDir.toString(), fileName) {

        @Override
        public void onSuccess(ResponseBody responseBody) {
            callBack.downLoadFileCompleted();
        }

        @Override
        public void progress(long progress) {
            LogUtil.e("progress: " + progress / 1024 + "kb  total: " + FileLoadEvent.getInstance().getTotal() / 1024 + "kb");
        }

        @Override
        public void onStart() {
            LogUtil.e("onStart");
        }

        @Override
        public void onCompleted() {
            LogUtil.e("onCompleted");
        }

        @Override
        public void onError(Throwable e) {
            if (e instanceof SocketTimeoutException) {
                LogUtil.e("SocketTimeoutException: 网络中断,请检查您的网络状态");
            } else if (e instanceof ConnectException) {
                LogUtil.e("ConnectException: 网络中断,请检查您的网络状态");
            } else if (e instanceof UnknownHostException) {
                LogUtil.e("UnknownHostException: 网络中断,请检查您的网络状态");
            } else {
                LogUtil.e("onError:其他错误:" + e.getMessage() + "  cause: " + e.getCause());
            }
            e.printStackTrace();
        }
    };
    //重写了ResponseBodyHttpClient
    String URL = "http://download.fir.im/v2/app/install/5818acbcca87a836f50014af?download_token=a01301d7f6f8f4957643c3fcfe5ba6ff";
    DownLoadHttpClient.getService(AppService.class).download(URL)
            .subscribeOn(Schedulers.io())//请求网络 在调度者的io线程
            .observeOn(Schedulers.io()) //指定线程保存文件
            .doOnNext(new Action1() {
                @Override
                public void call(ResponseBody body) {
                    downLoadCallback.saveFile(body);
                }
            })
            .observeOn(AndroidSchedulers.mainThread()) //在主线程中更新ui
            .compose(mActivity.bindUntilEvent(ActivityEvent.DESTROY))
            .subscribe(new FileSubscriber(downLoadCallback));
}

对网络结果的的保存,且不改变数据流的格式,一般在doOnext()方法中进行,并且设置在子线程中完成。需要进度的回调会在onprogress()方法中获得,成功后会回调onSuccess()方法,这样就完成了对文件的下载以及过程的进度监听。

 

无网络取缓存

无网络从缓存的文件中获取,是从Intercepter拦截器中实现的。自定义AcheIntercepter类如下所示:

public class AcheInterceptor implements Interceptor {
    public static final String CACHE = "Cache-Control:max-stale=" + (60 * 60 * 24 * 7);//离线缓存7天

    @Override
    public Response intercept(@NonNull Chain chain) throws IOException {
        Request request = chain.request();
        if (request.method().equals("GET")) {
            if (!NetworkUtil.isNetworkConnected(ContextUtil.getContext())) {
                request = request.newBuilder()
                        .cacheControl(new CacheControl.Builder().onlyIfCached()
                                .maxStale(request.cacheControl().maxStaleSeconds(), TimeUnit.SECONDS).build())
                        .build();
            }
            Response response = chain.proceed(request);
            if (NetworkUtil.isNetworkConnected(ContextUtil.getContext())) {
                response.newBuilder()
                        .header("Cache-Control", "Cache-Control:,max-age=0")
                        .removeHeader("Pragma")
                        .build();
            } else {
                response.newBuilder()
                        .header("Cache-Control", "Cache-Control:public, only-if-cached")
                        .removeHeader("Pragma")
                        .build();
            }
            return response;
        }
        return chain.proceed(request);
    }
}

这里我们只对GET方法做缓存处理,判断是否有网络,有网络取网络获取的数据,过期时间设置为0,没有网络取之前缓存下来的数据,并设置过期时间为7天。

接口类:

//无网络取缓存
@Headers(AcheInterceptor.CACHE)
@GET("https://www.mrallen.cn/test.php")
Observable> simpleGetCache();

使用:

/**
 * 无网络取缓存
 */
@Override
public void getSimpleCacheData(final LoadingCallBack callBack) {
    addActSubscribe(HttpClient.getService(AppService.class).simpleGetCache(), new RxRequestCallBack() {
        @Override
        public void onSuccess(HttpResult httpResult) {
            callBack.simpleCacheDataCompleted(httpResult.getData());
        }
    });
}

只需在GET方法上添加注解@headers(AcheIntercepter.CACHE),告诉这个接口采用无网络取缓存的策略就可以了,感觉还是挺方便的。

以上就是基于MVP模式封装的一套RxJava+Retrofit2+Okhttp3+Rxlifecycle框架了,代码的链接如下,有更好建议或者疑问的看官欢迎提出来互相交流学习。喜欢的看官可以给我点一个小星星吧,小小的鼓励,哈哈哈哈........

GitHub: https://github.com/634930172/JohnDevFrame

点击打开链接

CSDN: https://download.csdn.net/download/a634930172a/10489294

点击打开链接

你可能感兴趣的:(Android,Android,RxJava,Mvp,Retrofit,Rxlifecycle)