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

来源:互联网 发布:淘宝零食店货源哪里来 编辑:程序博客网 时间:2024/05/18 03:05

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<T extends BaseView> implements IPresenter<T> {    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> T create(Class<T> service) {        return mRetrofit.create(service);    }    /**     * rx订阅     */    public  <T> void toSubscribe(Observable<T> o, Subscriber<T> 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  <T> void toSubscribe(Observable<T> o, Subscriber<T> 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个类,如下:

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<T> {    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<T> extends Subscriber<T> 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<T> implements Func1<T,T> {    @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<View> implements Presenter {    public TestPresenterImpl(Context mContext) {        this.mContext = mContext;    }    @Override    public void getMovieListData(int start, int count) {        //获取数据        HashMap<String,String> map=new HashMap<>();        map.put("start", StringUtils.getString(start));        map.put("count", StringUtils.getString(count));        rx.Observable<MovieListBean> observable = RetrofitManager.getInstace().create(ApiService.class).getMovieListData(map).map((new HttpResultFunc<MovieListBean>()));        Subscription rxSubscription = new ProgressSubscriber<>(new SubscriberOnResponseListenter<MovieListBean>() {            @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源码地址

阅读全文
0 0
原创粉丝点击