打造终极MVP+Retrofit2+okhttp3+Rxjava

作者: 打酱油的日光灯 | 来源:发表于2018-10-15 17:56 被阅读641次

    打造终极MVP+Retrofit2+okhttp3+Rxjava2网络请求,开发实用,简约,由于篇幅字数原因 本章讲解实现MVP模式的代码结构

    抓住人生中的一分一秒,胜过虚度中的一月一年!

    前言

    目前最火的网络请求就是Retrofit+okhttp+Rxjava,于是我也加入了使用行列,在网上找了许多案例,实际代码开发中解决了一些所谓的坑,打造出一个使用简约的框架封装,mvp实现模式,实现逻辑如下。

    利用了以下技术点
    1.Retrofit2 Retrofit2官网
    2.okhttp3 okhttp3官网
    3.RxJava2 RxJava2官网
    4.MVP

    下面看下代码结构

    mmm.png
    最终实现效果如下图
    ccc.gif

    1.导包

     //网络请求
        compile 'com.squareup.okhttp3:okhttp:3.9.1'
        compile 'com.squareup.retrofit2:retrofit:2.3.0'
        //ConverterFactory的Gson依赖包
        compile 'com.squareup.retrofit2:converter-gson:2.3.0'
        //CallAdapterFactory的Rx依赖包
        compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
    
        compile 'io.reactivex.rxjava2:rxandroid:2.0.2'
    

    2.retrofit代码实现

    import com.google.gson.Gson;
    import com.google.gson.GsonBuilder;
    import com.lp.mvp_network.App;
    import com.lp.mvp_network.base.BaseContent;
    import com.lp.mvp_network.base.convert.MyGsonConverterFactory;
    import com.lp.mvp_network.base.cookie.CookieManger;
    import com.lp.mvp_network.base.gson.DoubleDefault0Adapter;
    import com.lp.mvp_network.base.gson.IntegerDefault0Adapter;
    import com.lp.mvp_network.base.gson.LongDefault0Adapter;
    import com.orhanobut.logger.Logger;
    
    import java.io.IOException;
    import java.util.concurrent.TimeUnit;
    
    import okhttp3.Interceptor;
    import okhttp3.MediaType;
    import okhttp3.OkHttpClient;
    import okhttp3.Request;
    import okhttp3.Response;
    import okhttp3.ResponseBody;
    import retrofit2.Retrofit;
    import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
    import retrofit2.converter.gson.GsonConverterFactory;
    
    /**
     * File descripition:  创建Retrofit
     *
     * @author lp
     * @date 2018/6/19
     */
    
    public class ApiRetrofit {
        public final String BASE_SERVER_URL = BaseContent.baseUrl;
        private String TAG = "ApiRetrofit %s";
        private static ApiRetrofit apiRetrofit;
        private Retrofit retrofit;
        private ApiServer apiServer;
        private static Gson gson;
        private static final int DEFAULT_TIMEOUT = 15;
    
    
        public ApiRetrofit() {
            OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
            httpClientBuilder
                    .cookieJar(new CookieManger(App.getContext())) //这块是添加的管理cookie方法
                    .addInterceptor(interceptor)//日志拦截
                    .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                    .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                    .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                    .retryOnConnectionFailure(true);//错误重联
    
            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_SERVER_URL)
                    .addConverterFactory(GsonConverterFactory.create())//添加json转换框架
                    //支持RxJava2
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .client(httpClientBuilder.build())
                    .build();
            apiServer = retrofit.create(ApiServer.class);
        }
    
        public static ApiRetrofit getInstance() {
            if (apiRetrofit == null) {
                synchronized (Object.class) {
                    if (apiRetrofit == null) {
                        apiRetrofit = new ApiRetrofit();
                    }
                }
            }
            return apiRetrofit;
        }
    
        public ApiServer getApiService() {
            return apiServer;
        }
    
        /**
         * 请求访问quest
         * response拦截器  打印日志
         */
        private Interceptor interceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
                long startTime = System.currentTimeMillis();
                Response response = chain.proceed(chain.request());
                long endTime = System.currentTimeMillis();
                long duration = endTime - startTime;
                MediaType mediaType = response.body().contentType();
                String content = response.body().string();
    
                Logger.wtf(TAG, "----------Request Start----------------");
                Logger.e(TAG, "| " + request.toString() + "===========" + request.headers().toString());
                Logger.json(content);
                Logger.e(content);
                Logger.wtf(TAG, "----------Request End:" + duration + "毫秒----------");
    
                return response.newBuilder()
                        .body(ResponseBody.create(mediaType, content))
                        .build();
            }
        };
    }
    

    4.新建ApiServer类,注释的都是请求案例

    public interface ApiServer {
        //示例    多种类型请求方式
    
    //    @POST("api/Activity/get_activities?")
    //    Observable<BaseModel<List<>>> getApi1(@Query("time") String requestType);
    
    //    @GET("api/Activity/get_activities?")
    //    Observable<BaseModel<List<>>> getApi1(@Query("time") String requestType);
    
    //    @FormUrlEncoded
    //    @POST("api/Activity/get_activities?")
    //    Observable<BaseModel<List<>>> getApi1(@Field("time") String requestType);
    
    //    @FormUrlEncoded
    //    @POST("api/Activity/get_activities?")
    //    Observable<BaseModel<List<>>> getApi1(@FieldMap HashMap<String, String> params);
    
    //    @Multipart
    //    @POST("api/Activity/get_activities?")
    //    Observable<BaseModel<List<>>> getApi1(@PartMap Map<String, RequestBody> map);
    
        @POST("api/Activity/get_activities?")
        Observable<BaseModel<List<MainBean>>> getMain(@Query("time") String requestType);
    }
    

    5.创建BaseView基类,用于添加自定义回调(复制粘贴即可使用)

    /**
     * File descripition:   基本回调 可自定义添加所需回调
     *
     * @author lp
     * @date 2018/6/19
     */
    
    public interface BaseView {
        /**
         * 显示dialog
         */
        void showLoading();
        /**
         * 隐藏 dialog
         */
    
        void hideLoading();
        /**
         * 显示错误信息
         *
         * @param msg
         */
        void showError(String msg);
        /**
         * 错误码
         */
        void onErrorCode(BaseModel model);
    }
    

    6.创建Presenter基类,于view通信的桥梁(复制粘贴即可使用)

    import com.lp.mvp_network.api.ApiRetrofit;
    import com.lp.mvp_network.api.ApiServer;
    
    import io.reactivex.Observable;
    import io.reactivex.android.schedulers.AndroidSchedulers;
    import io.reactivex.disposables.CompositeDisposable;
    import io.reactivex.schedulers.Schedulers;
    /**
     * File descripition:   创建Presenter基类
     *
     * @author lp
     * @date 2018/6/19
     */
    public class BasePresenter<V extends BaseView> {
        private CompositeDisposable compositeDisposable;
        public V baseView;
        protected ApiServer apiServer = ApiRetrofit.getInstance().getApiService();
    
        public BasePresenter(V baseView) {
            this.baseView = baseView;
        }
        /**
         * 解除绑定
         */
        public void detachView() {
            baseView = null;
            removeDisposable();
        }
        /**
         * 返回 view
         *
         * @return
         */
        public V getBaseView() {
            return baseView;
        }
    
        public void addDisposable(Observable<?> observable, BaseObserver observer) {
            if (compositeDisposable == null) {
                compositeDisposable = new CompositeDisposable();
            }
            compositeDisposable.add(observable.subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribeWith(observer));
        }
    
        public void removeDisposable() {
            if (compositeDisposable != null) {
                compositeDisposable.dispose();
            }
        }
    }
    

    8.创建实体类基类,与服务器约定返回固定字段内容

    import java.io.Serializable;
    
    /**
     * File descripition:   mode基类
     *
     * @author lp
     * @date 2018/6/19
     */
    public class BaseModel<T> implements Serializable {
        private String msg;
        private int code;
        private T data;
    
        public BaseModel(String message, int code) {
            this.msg = message;
            this.code = code;
        }
    
        public int getErrcode() {
            return code;
        }
    
        public void setErrcode(int code) {
            this.code = code;
        }
    
        public String getErrmsg() {
            return msg;
        }
    
        public void setErrmsg(String message) {
            this.msg = message;
        }
    
        public T getData() {
            return data;
        }
    
        public void setData(T result) {
            this.data = result;
        }
    
        @Override
        public String toString() {
            return "BaseModel{" +
                    "code=" + code +
                    ", msg='" + msg + '\'' +
                    ", result=" + data +
                    '}';
        }
    }
    

    9.较为重要的一步,数据处理基类

    import com.google.gson.JsonParseException;
    import com.lp.mvp_network.base.BaseContent;
    import com.lp.mvp_network.utils.NetWorkUtils;
    import org.json.JSONException;
    import java.io.InterruptedIOException;
    import java.net.ConnectException;
    import java.net.UnknownHostException;
    import java.text.ParseException;
    import io.reactivex.observers.DisposableObserver;
    import retrofit2.HttpException;
    /**
     * File descripition:   数据处理基类
     *
     * @author lp
     * @date 2018/6/19
     */
    public abstract class BaseObserver<T> extends DisposableObserver<T> {
        /**
         * 于服务器约定  返回code为几是正常请求
         */
        public static final int CODE = BaseContent.basecode;
        protected BaseView view;
        /**
         * 网络连接失败  无网
         */
        public static final int NETWORK_ERROR = 100000;
        /**
         * 解析数据失败
         */
        public static final int PARSE_ERROR = 1008;
        /**
         * 网络问题
         */
        public static final int BAD_NETWORK = 1007;
        /**
         * 连接错误
         */
        public static final int CONNECT_ERROR = 1006;
        /**
         * 连接超时
         */
        public static final int CONNECT_TIMEOUT = 1005;
        public BaseObserver(BaseView view) {
            this.view = view;
        }
        @Override
        protected void onStart() {
            if (view != null) {
                view.showLoading();
            }
        }
        @Override
        public void onNext(T o) {
            try {
                // loading写到这里没有延迟效果
                if (view != null) {
                    view.hideLoading();
                }
                BaseModel model = (BaseModel) o;
                if (model.getErrcode() == CODE) {
                    onSuccess(o);
                } else {
                    if (view != null) {
                        view.onErrorCode(model);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                onError(e.toString());
            }
        }
        @Override
        public void onError(Throwable e) {
            if (view != null) {
                view.hideLoading();
            }
            if (e instanceof HttpException) {
                //   HTTP错误
                onException(BAD_NETWORK, "");
            } else if (e instanceof ConnectException
                    || e instanceof UnknownHostException) {
                //   连接错误
                onException(CONNECT_ERROR, "");
            } else if (e instanceof InterruptedIOException) {
                //  连接超时
                onException(CONNECT_TIMEOUT, "");
            } else if (e instanceof JsonParseException
                    || e instanceof JSONException
                    || e instanceof ParseException) {
                //  解析错误
                onException(PARSE_ERROR, "");
                e.printStackTrace();
            }  else {
                if (e != null) {
                    onError(e.toString());
                } else {
                    onError("未知错误");
                }
            }
        }
        /**
         * 中间拦截一步  判断是否有网络  为确保准确  此步去除也可以
         *
         * @param unknownError
         * @param message
         */
        private void onException(int unknownError, String message) {
            BaseModel model = new BaseModel(message, unknownError);
            if (!NetWorkUtils.isAvailableByPing()) {
                model.setErrcode(NETWORK_ERROR);
                model.setErrmsg("网络不可用,请检查网络连接!");
            }
            onExceptions(model.getErrcode(), model.getErrmsg());
            if (view != null) {
                view.onErrorCode(model);
            }
        }
        private void onExceptions(int unknownError, String message) {
            switch (unknownError) {
                case CONNECT_ERROR:
                    onError("连接错误");
                    break;
                case CONNECT_TIMEOUT:
                    onError("连接超时");
                    break;
                case BAD_NETWORK:
                    onError("网络超时");
                    break;
                case PARSE_ERROR:
                    onError("数据解析失败");
                    break;
                //网络不可用
                case NETWORK_ERROR:
                    onError("网络不可用,请检查网络连接!");
                    break;
                default:
                    break;
            }
        }
        //loading消失写到这 有一定的延迟  对dialog显示有影响
        @Override
        public void onComplete() {
           /* if (view != null) {
                view.hideLoading();
            }*/
        }
        public abstract void onSuccess(T o);
        public abstract void onError(String msg);
    }
    

    至此,请求内部模块已经写完,以下内容为如何请求

    10.所需请求代码封装到activity基类,这样在activity省略了很多重复代码,对代码的阅读性提升了很多

    import android.content.Context;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    
    import com.lp.mvp_network.base.mvp.BaseModel;
    import com.lp.mvp_network.base.mvp.BasePresenter;
    import com.lp.mvp_network.base.mvp.BaseView;
    import com.lp.mvp_network.promptdialog.PromptDialog;
    import com.lp.mvp_network.utils.StatusBarUtil;
    
    import static com.lp.mvp_network.base.mvp.BaseObserver.NETWORK_ERROR;
    
    /**
     * File descripition: activity基类
     * <p>
     *
     * @author lp
     * @date 2018/5/16
     */
    public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity implements BaseView {
        protected final String TAG = this.getClass().getSimpleName();
        public Context mContext;
        protected P mPresenter;
        protected abstract P createPresenter();
        //错误提示框  警告框  成功提示框 加载进度框 (只是提供个案例 可自定义)
        private PromptDialog promptDialog;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mContext = this;
            setContentView(getLayoutId());
            mPresenter = createPresenter();
            setStatusBar();
    
            this.initToolbar(savedInstanceState);
            this.initData();
        }
        /**
         * 获取布局ID
         *
         * @return
         */
        protected abstract int getLayoutId();
        /**
         * 处理顶部title
         *
         * @param savedInstanceState
         */
        protected abstract void initToolbar(Bundle savedInstanceState);
        /**
         * 数据初始化操作
         */
        protected abstract void initData();
        /**
         * 此处设置沉浸式地方
         */
        protected void setStatusBar() {
            StatusBarUtil.setTranslucentForImageViewInFragment(this, 0, null);
        }
        /**
         * 封装toast方法(自行去实现)
         *
         * @param str
         */
        public void showToast(String str) {
        }
    
        public void showLongToast(String str) {
        }
        @Override
        public void showError(String msg) {
            showToast(msg);
        }
        /**
         * 返回所有状态  除去指定的值  可设置所有(根据需求)
         *
         * @param model
         */
        @Override
        public void onErrorCode(BaseModel model) {
            if (model.getErrcode() == NETWORK_ERROR) {
    
            }
        }
        //显示加载进度框回调
        @Override
        public void showLoading() {
            showLoadingDialog();
        }
        //隐藏进度框回调
        @Override
        public void hideLoading() {
            closeLoadingDialog();
        }
        /**
         * 进度款消失
         */
        public void closeLoadingDialog() {
            if (promptDialog != null) {
                promptDialog.dismiss();
            }
        }
        /**
         * 加载中...
         */
        public void showLoadingDialog() {
            if (promptDialog == null) {
                promptDialog = new PromptDialog(this);
            }
            promptDialog.showLoading("加载中...",false);
        }
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (mPresenter != null) {
                mPresenter.detachView();
            }
        }
    }
    

    以上内容为MVP+Retrofit2+okhttp3+Rxjava2全部封装,其间有些地方需根据自己项目内容所做修改,下边为大家演示下如何在对应activity请求数据

    请求时有三个步骤,步骤如下

    1.新建接口实体类,注意内容,抛去baseModel里边的内容,也就是抛去每个接口固定返回的字段,如code,message
    比如实体类如下

    /**
     * File descripition:
     *
     * @author lp
     * @date 2018/9/19
     */
    public class MainBean {
        /**
         * id : 11
         * act_logo : http://www.energy-link.com.cn/upload/admin/20180828/s_29a692567d0f0d84d515eb5cf5be98d0.jpg
         * play_time : 2018-06-10
         * name : 中国生物质能源产业联盟会员代表大会
         * province : 北京市
         * city : 西城区
         */
        private int id;
        private String act_logo;
        private String play_time;
        private String name;
        private String province;
        private String city;
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getAct_logo() {
            return act_logo;
        }
        public void setAct_logo(String act_logo) {
            this.act_logo = act_logo;
        }
    
        public String getPlay_time() {
            return play_time;
        }
        public void setPlay_time(String play_time) {
            this.play_time = play_time;
        }
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
        public String getProvince() {
            return province;
        }
        public void setProvince(String province) {
            this.province = province;
        }
        public String getCity() {
            return city;
        }
        public void setCity(String city) {
            this.city = city;
        }
    }
    

    2.新建对应的接口回调view

    import com.lp.mvp_network.base.mvp.BaseModel;
    import com.lp.mvp_network.base.mvp.BaseView;
    import java.util.List;
    /**
     * File descripition:
     *
     * @author lp
     * @date 2018/6/19
     */
    public interface MainView extends BaseView {
        void onMainSuccess(BaseModel<List<MainBean>> o);
    }
    

    3.新建对应的请求Presenter

    import com.lp.mvp_network.base.mvp.BaseModel;
    import com.lp.mvp_network.base.mvp.BaseObserver;
    import com.lp.mvp_network.base.mvp.BasePresenter;
    
    import java.util.List;
    
    /**
     * File descripition:
     *
     * @author lp
     * @date 2018/6/19
     */
    
    public class MainPresenter extends BasePresenter<MainView> {
        public MainPresenter(MainView baseView) {
            super(baseView);
        }
    
        public void commentAdd() {
            addDisposable(apiServer.getMain("year"), new BaseObserver(baseView) {
                @Override
                public void onSuccess(Object o) {
                    baseView.onMainSuccess((BaseModel<List<MainBean>>) o);
                }
    
                @Override
                public void onError(String msg) {
                    if (baseView != null) {
                        baseView.showError(msg);
                    }
                }
            });
        }
    }
    

    4.在activity实现Presenter,比如mainActivity

    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    import com.lp.mvp_network.R;
    import com.lp.mvp_network.base.BaseActivity;
    import com.lp.mvp_network.base.mvp.BaseModel;
    import java.util.List;
    public class MainActivity extends BaseActivity<MainPresenter> implements MainView, View.OnClickListener {
        private TextView tv_msg;
        private Button btn;
        @Override
        protected MainPresenter createPresenter() {
            return new MainPresenter(this);
        }
        @Override
        protected int getLayoutId() {
            return R.layout.activity_main;
        }
        @Override
        protected void initToolbar(Bundle savedInstanceState) {
    
        }
        @Override
        protected void initData() {
            tv_msg = findViewById(R.id.tv_msg);
            btn = findViewById(R.id.btn);
            btn.setOnClickListener(this);
        }
        @Override
        public void onMainSuccess(BaseModel<List<MainBean>> o) {
            //数据返回
            tv_msg.setText(o.getData().toString());
        }
        @Override
        public void onClick(View v) {
            //数据请求
            mPresenter.commentAdd();
        }
    }
    

    MVP+Retrofit2+okhttp3+Rxjava2网络请求封装完成 ,但还有遗留一个问题,因为开发中有这样的需求 ,当服务器返回假如0是正常 1是不正常 当返回0时:我们gson 或 fastJson解析数据,返回1时:我们不想解析(可能返回值出现以前是对象 但是现在数据为空变成了数组等等,也就是于是在不改后台代码的情况下 我们前端需要处理),此问题请看下章
    本文章最终实现终极MVP+Retrofit2+okhttp3+Rxjava2网络请求,开发实用,简约,具体实现方式请查看下篇文章

    github稍后更新

    相关文章

      网友评论

      本文标题:打造终极MVP+Retrofit2+okhttp3+Rxjava

      本文链接:https://www.haomeiwen.com/subject/rgofzftx.html