(四)安卓框架搭建之MVP+Retrofit+RxJava优化

BasePresenter的优化

RxJava也需要管理生命周期,即添加订阅和解除订阅。这里我们使之与presenter的addtachView()和detachView()同步,修改BasePresenter里面内容如下:

package com.example.burro.demo.appframework.mvp.presenter;

import com.example.burro.demo.appframework.mvp.view.BaseView;

import rx.Subscription;
import rx.subscriptions.CompositeSubscription;

/**
 * Presenter基类。目的是统一处理绑定和解绑
 * Created by ex.zhong on 2017/9/23.
 */
public class BasePresenter implements IPresenter {

    protected T mView;
    protected CompositeSubscription mCompositeSubscription;

    @Override
    public void attachView(T mView) {
        mView = mView;
    }

    @Override
    public void detachView() {
        mView = null;
        unSubscribe();
    }

    //增加订阅者
    protected void addSubscrebe(Subscription subscription) {
        if (mCompositeSubscription == null) {
            mCompositeSubscription = new CompositeSubscription();
        }
        mCompositeSubscription.add(subscription);
    }
    //解绑订阅者
    protected void unSubscribe() {
        if (mCompositeSubscription != null) {
            mCompositeSubscription.unsubscribe();
        }
    }

}

RetrofitManager的封装

看下先前TestPresenterImpl中的getMovieListData()内容,每次请求数据都重复新建一个Retrofit,绝对不能接受的,因此我们要对retrofit进行统一的封装,其中需使用单例模式。类中注释比较清楚,直接看下面内容:

package com.example.burro.demo.appframework.http;

/**
 * Created by ex.zhong on 2017/9/24.
 */

import com.example.burro.demo.appframework.BaseApplication;
import com.example.burro.demo.appframework.util.FileUtils;
import com.example.burro.demo.appframework.util.LogUtils;
import com.example.burro.demo.appframework.util.NetWorkUtils;

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

import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;

/**
 * retrofit封装方法
 * Created by ex.zhong on 2017/8/13.
 */
public class RetrofitManager {
    private static RetrofitManager instance;
    public static int MAXSTALE = 60 * 60 * 24 * 28; // 无网络时,设置超时为4周
    public static int CONNECT_OUTTIME = 10; // 链接超时时间 unit:S
    public static int READ_OUTTIME = 20; // 读取数据超时时间 unit:S
    public static int WRITE_OUTTIME = 20; // 写入超时时间 unit:S
    public static long CACHE_SIZE = 1024*1024*50; // 缓存大小 50M
    private final OkHttpClient mOkHttpClient;
    private final Retrofit mRetrofit;

    /**
     * 创建单例
     */
    public static RetrofitManager getInstace() {
        if (instance == null) {
            synchronized (RetrofitManager.class) {
                instance = new RetrofitManager();
            }
        }
        return instance;
    }

    /**
     * 获取retrofit
     */
    public Retrofit getRetrofit() {
        return mRetrofit;
    }

    /**
     * 创建服务类
     * @return
     */
    public  T create(Class service) {
        return mRetrofit.create(service);
    }

    /**
     * rx订阅
     */
    public   void toSubscribe(Observable o, Subscriber s) {

        o.subscribeOn(Schedulers.io())

                .unsubscribeOn(Schedulers.io())

                .observeOn(AndroidSchedulers.mainThread())

                .subscribe(s);

    }

    private RetrofitManager() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        //打印日志
        if(LogUtils.LOG_FLAG){
            // https://drakeet.me/retrofit-2-0-okhttp-3-0-config
            HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
            loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
            builder.addInterceptor(loggingInterceptor);
        }
        //设置缓存
        File cacheFile=new File(FileUtils.getInstance().getHttpCachePath());
        Cache cache=new Cache(cacheFile,CACHE_SIZE);
        Interceptor cacheInterceptor=new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
                if (!NetWorkUtils.isOnline(BaseApplication.getAppContext())) {
                    request = request.newBuilder()
                            .cacheControl(CacheControl.FORCE_CACHE)
                            .build();
                }
                Response response = chain.proceed(request);
                if (NetWorkUtils.isOnline(BaseApplication.getAppContext())) {
                    int maxAge = 0;
                    response.newBuilder()
                            .header("Cache-Control", "public, max-age=" + maxAge)
                            .removeHeader("Pragma")
                            .build();
                } else {
                    response.newBuilder()
                            .header("Cache-Control", "public, only-if-cached, max-stale=" + MAXSTALE)
                            .removeHeader("Pragma")
                            .build();
                }
                return response;
            }
        };
        //设置缓存
        builder.addNetworkInterceptor(cacheInterceptor);
        builder.addInterceptor(cacheInterceptor);
        builder.cache(cache);
        //设置超时
        builder.connectTimeout(CONNECT_OUTTIME, TimeUnit.SECONDS);
        builder.readTimeout(READ_OUTTIME, TimeUnit.SECONDS);
        builder.writeTimeout(WRITE_OUTTIME, TimeUnit.SECONDS);
        //错误重连
        builder.retryOnConnectionFailure(true);
        mOkHttpClient = builder.build();
        mRetrofit = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(HttpConfig.BASE_URL)
                .client(mOkHttpClient)
                .build();

    }

}

这里注意的是我把rxjava的订阅也放在了RetrofitManager内,这样不必每次在presenter实现类里重复的写下面方法

public   void toSubscribe(Observable o, Subscriber s) {
       o.subscribeOn(Schedulers.io())
               .unsubscribeOn(Schedulers.io())
               .observeOn(AndroidSchedulers.mainThread())
               .subscribe(s);
   }

Subscriber的封装

我们再看时解决每次重写Subscriber的这四个onstart()、onCompleted()、onError()、onNext()的问题,在此大致的说一下思路了,创建一个ProgressSubscriber.java让它继承自Subscriber,并添加网络请求时所需要的加载框,在onStart()方法中显示加载框【加载框我会传一个参数来控制是否需要加载】,在onCompleted()、onError()隐藏加载框,再通过接口回调将onNext()中产生的数据回调给presenter中去通知UI更新就行,
这里一共新建了5个类,如下:

(四)安卓框架搭建之MVP+Retrofit+RxJava优化_第1张图片
ProgressCancelListener

主要是dialog取消时的回调时的接口,会在取消时执行解绑操作

package com.example.burro.demo.appframework.http;
/*progressDialog,消失时回调
 *Created by ex.zhong on 2017/9/25.
 */
public interface ProgressCancelListener {
    void onCancelProgress();
}

SubscriberOnResponseListenter

主要是Subscriber回调时封装的接口,即在回调中只处理onNext()onError()

package com.example.burro.demo.appframework.http;


import com.example.burro.demo.dataframework.model.BaseResultBean;

/**Subscriber回调,统一归结为next()【成功】 error()【失败】
 * Created by ex.zhong on 2017/9/25
 */
public interface SubscriberOnResponseListenter {
    void next(T t);
    void error(BaseResultBean t);
}


ProgressDialogHandler

ProgressDialogHandler实为handler,内部主要控制dialog的显示和隐藏

package com.example.burro.demo.appframework.http;

import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.os.Handler;
import android.os.Message;

import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.Theme;


/** 加载框展示,隐藏控制
 * Created by ex.zhong on 2017/9/25.
 */
public class ProgressDialogHandler extends Handler {
    public static final int SHOW_PROGRESS_DIALOG = 1;
    public static final int DISMISS_PROGRESS_DIALOG = 2;
    private Context context;
    private boolean cancelable;
    private ProgressCancelListener mProgressCancelListener;

    private MaterialDialog mProgressDialog;

    public ProgressDialogHandler(Context context, ProgressCancelListener mProgressCancelListener, boolean cancelable) {
        super();
        this.context = context;
        this.mProgressCancelListener = mProgressCancelListener;
        this.cancelable = cancelable;
    }
    //显示dialog
    private void initProgressDialog() {
        if (mProgressDialog == null) {
            mProgressDialog = new MaterialDialog.Builder(context)
                    .canceledOnTouchOutside(cancelable)
                    .content("正在加载...")
                    .progress(true, 0)
                    .theme(Theme.LIGHT)
                    .build();
            mProgressDialog.setOnCancelListener(new OnCancelListener() {
                @Override
                public void onCancel(DialogInterface dialogInterface) {
                    mProgressCancelListener.onCancelProgress();
                }
            });
            if (!mProgressDialog.isShowing()) {
                mProgressDialog.show();
            }
        }
    }
    //隐藏dialog
    private void dismissProgressDialog() {
        if (mProgressDialog != null) {
            mProgressDialog.dismiss();
        }
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {

            case SHOW_PROGRESS_DIALOG:
                initProgressDialog();
                break;

            case DISMISS_PROGRESS_DIALOG:
                dismissProgressDialog();
                break;

        }
    }
}

ProgressSubscriber

ProgressSubscriber继承Subscriber,是对Subscriber的进一步封装,即在请求开始时提示dialog【可控制】,完成时,隐藏dialog等

package com.example.burro.demo.appframework.http;

import android.content.Context;
import android.net.ParseException;

import com.example.burro.demo.dataframework.model.BaseResultBean;
import com.google.gson.JsonParseException;

import org.apache.http.conn.ConnectTimeoutException;
import org.json.JSONException;

import java.net.ConnectException;
import java.net.SocketTimeoutException;

import retrofit2.adapter.rxjava.HttpException;
import rx.Subscriber;

/** 并添加网络请求时所需要的加载框,异常情况统一处理
 * Created by ex.zhong on 2017/9/25.
 */
public class ProgressSubscriber extends Subscriber implements ProgressCancelListener {

    private SubscriberOnResponseListenter mSubscriberOnResponseListenter;
    private ProgressDialogHandler mProgressDialogHandler;
    private boolean isShowProgress;

    public ProgressSubscriber(SubscriberOnResponseListenter mSubscriberOnResponseListenter, Context context, boolean isShowProgress) {
        this.mSubscriberOnResponseListenter = mSubscriberOnResponseListenter;
        this.isShowProgress = isShowProgress;
        mProgressDialogHandler = new ProgressDialogHandler(context, this, false);
    }

    /**
     * 开始订阅的时候显示加载框
     */
    @Override
    public void onStart() {
        if (isShowProgress)
            showProgressDialog();
    }

    @Override
    public void onCompleted() {
        dismissProgressDialog();
    }

    @Override
    public void onError(Throwable e) {
        dismissProgressDialog();
        BaseResultBean errorBean;
        //错误码要以服务器返回错误码为准。此处只是举例
        if (e instanceof HttpException) {             //HTTP 错误
            HttpException httpException = (HttpException) e;
            switch (httpException.code()) {
                case BaseResultBean.ERROR_CODE_UNAUTHORIZED:
                    errorBean = new BaseResultBean(BaseResultBean.ERROR_CODE_UNAUTHORIZED, "当前请求需要用户验证");
                    break;
                case BaseResultBean.ERROR_CODE_FORBIDDEN:
                    errorBean = new BaseResultBean(BaseResultBean.ERROR_CODE_FORBIDDEN, "但是拒绝执行它");
                    break;
                case BaseResultBean.ERROR_CODE_NOT_FOUND:
                    errorBean = new BaseResultBean(BaseResultBean.ERROR_CODE_NOT_FOUND, "服务器异常,请稍后再试");
                    break;
                default:
                    //其它均视为网络错误
                    errorBean = new BaseResultBean(BaseResultBean.ERROR_CODE_FORBIDDEN, "网络错误");
                    break;
            }
        } else if (e instanceof JsonParseException
                || e instanceof JSONException
                || e instanceof ParseException) {
            errorBean = new BaseResultBean(BaseResultBean.ERROR_CODE_PARSE_JSON, "解析错误");
        } else if (e instanceof ConnectException || e instanceof SocketTimeoutException || e instanceof ConnectTimeoutException) {
            errorBean = new BaseResultBean(BaseResultBean.ERROR_CODE_NETWORK, "网络连接失败,请检查是否联网");
        } else {
            errorBean = new BaseResultBean(BaseResultBean.ERROR_CODE_UNKNOWN, "未知错误");
        }
        mSubscriberOnResponseListenter.error(errorBean);
    }
    //成功执行下一步
    @Override
    public void onNext(T t) {
        mSubscriberOnResponseListenter.next(t);
    }

    @Override
    public void onCancelProgress() {
        if (!this.isUnsubscribed()) {
            this.unsubscribe();
        }
    }
    //显示dialog
    private void showProgressDialog() {
        if (mProgressDialogHandler != null) {
            mProgressDialogHandler.obtainMessage(ProgressDialogHandler.SHOW_PROGRESS_DIALOG).sendToTarget();
        }
    }
    //隐藏dialog
    private void dismissProgressDialog() {
        if (mProgressDialogHandler != null) {
            mProgressDialogHandler.obtainMessage(ProgressDialogHandler.DISMISS_PROGRESS_DIALOG).sendToTarget();
        }
    }
}


HttpResultFunc

HttpResultFunc对返回的结果进行预处理。也是链式结构中重要的一环。具体请看注释:

package com.example.framework.appframework.http;

import com.example.framework.dataframework.model.BaseResultBean;

import rx.functions.Func1;

/**预处理:
 * 由于我们每次请求的时候有可能会出现一些请求的错误,
 * 若返回码非正确,且服务器返回的错误信息可能是用户读不懂的信息。
 * 比如豆瓣一个错误码1001 msg信息为uri_not_found,此时我们可以转换成用户可以读懂的文字,即可设置msg为“资源不存在”
 * Created by ex.zhong on 2017/9/25.
 */
public class HttpResultFunc implements Func1 {
    @Override
    public T call(T t) {
        if(t!=null&&t instanceof BaseResultBean){
            int code=((BaseResultBean) t).getCode();
            switch (code){
                case 1001:
                    ((BaseResultBean) t).setMessage("资源不存在");
                    break;
                case 1002:
                    ((BaseResultBean) t).setMessage("参数不全");
                    break;
                //这里不再全部写出,实际根据情况写自己的业务处理
            }
        }
        return t;
    }
}

好了,封装部分全部结束,我们回头过来看看TestPresenterImpl

package com.example.burro.demo.appbiz.test;

import android.content.Context;

import com.example.burro.demo.appframework.http.HttpResultFunc;
import com.example.burro.demo.appframework.http.ProgressSubscriber;
import com.example.burro.demo.appframework.http.RetrofitManager;
import com.example.burro.demo.appframework.http.SubscriberOnResponseListenter;
import com.example.burro.demo.appframework.mvp.presenter.BasePresenter;
import com.example.burro.demo.appbiz.test.TestContract.*;
import com.example.burro.demo.appframework.util.StringUtils;
import com.example.burro.demo.databiz.model.test.MovieListBean;
import com.example.burro.demo.databiz.service.ApiService;
import com.example.burro.demo.dataframework.model.BaseResultBean;

import java.util.HashMap;

import rx.Subscriber;
import rx.Subscription;

/**测试presenter
 * Created by ex.zhong on 2017/9/23.
 */
public class TestPresenterImpl extends BasePresenter implements Presenter {

    public TestPresenterImpl(Context mContext) {
        this.mContext = mContext;
    }
    @Override
    public void getMovieListData(int start, int count) {
        //获取数据
        HashMap map=new HashMap<>();
        map.put("start", StringUtils.getString(start));
        map.put("count", StringUtils.getString(count));
        rx.Observable observable = RetrofitManager.getInstace().create(ApiService.class).getMovieListData(map).map((new HttpResultFunc()));
        Subscription rxSubscription = new ProgressSubscriber<>(new SubscriberOnResponseListenter() {
            @Override
            public void next(MovieListBean testBean) {
                mView.setMovieListData(testBean);
            }
            @Override
            public void error(BaseResultBean errResponse) {
                mView.showError(errResponse);
            }
        },mContext,false);
        RetrofitManager.getInstace().toSubscribe(observable, (Subscriber) rxSubscription);
        addSubscrebe(rxSubscription);
    }
}

是不是简单了许多!!注意封装完以后要查看在TestActivity页面是否打印出请求结果!

相关链接

(五)安卓框架搭建之BaseFragment,MainActivity, Toolbar细化

github源码地址

你可能感兴趣的:((四)安卓框架搭建之MVP+Retrofit+RxJava优化)