安卓框架搭建之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;@Overridepublic void attachView(T mView) {mView = mView;}@Overridepublic 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:Spublic static int READ_OUTTIME = 20; // 读取数据超时时间 unit:Spublic static int WRITE_OUTTIME = 20; // 写入超时时间 unit:Spublic static long CACHE_SIZE = 1024*1024*50; // 缓存大小 50Mprivate 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-configHttpLoggingInterceptor 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() {@Overridepublic 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个类,如下:

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;}//显示dialogprivate void initProgressDialog() {if (mProgressDialog == null) {mProgressDialog = new MaterialDialog.Builder(context).canceledOnTouchOutside(cancelable).content("正在加载...").progress(true, 0).theme(Theme.LIGHT).build();mProgressDialog.setOnCancelListener(new OnCancelListener() {@Overridepublic void onCancel(DialogInterface dialogInterface) {mProgressCancelListener.onCancelProgress();}});if (!mProgressDialog.isShowing()) {mProgressDialog.show();}}}//隐藏dialogprivate void dismissProgressDialog() {if (mProgressDialog != null) {mProgressDialog.dismiss();}}@Overridepublic 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);}/** * 开始订阅的时候显示加载框 */@Overridepublic void onStart() {if (isShowProgress)showProgressDialog();}@Overridepublic void onCompleted() {dismissProgressDialog();}@Overridepublic 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);}//成功执行下一步@Overridepublic void onNext(T t) {mSubscriberOnResponseListenter.next(t);}@Overridepublic void onCancelProgress() {if (!this.isUnsubscribed()) {this.unsubscribe();}}//显示dialogprivate void showProgressDialog() {if (mProgressDialogHandler != null) {mProgressDialogHandler.obtainMessage(ProgressDialogHandler.SHOW_PROGRESS_DIALOG).sendToTarget();}}//隐藏dialogprivate 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 {@Overridepublic 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;}@Overridepublic 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() {@Overridepublic void next(MovieListBean testBean) {mView.setMovieListData(testBean);}@Overridepublic void error(BaseResultBean errResponse) {mView.showError(errResponse);}},mContext,false);RetrofitManager.getInstace().toSubscribe(observable, (Subscriber) rxSubscription);addSubscrebe(rxSubscription);}
}

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


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部