打造终极MVP+Retrofit2+okhttp3+Rxjava
打造终极MVP+Retrofit2+okhttp3+Rxjava2网络请求,开发实用,简约,由于篇幅字数原因 本章讲解实现MVP模式的代码结构
抓住人生中的一分一秒,胜过虚度中的一月一年!
前言
目前最火的网络请求就是Retrofit+okhttp+Rxjava,于是我也加入了使用行列,在网上找了许多案例,实际代码开发中解决了一些所谓的坑,打造出一个使用简约的框架封装,mvp实现模式,实现逻辑如下。
利用了以下技术点
1.Retrofit2 Retrofit2官网
2.okhttp3 okhttp3官网
3.RxJava2 RxJava2官网
4.MVP
下面看下代码结构
最终实现效果如下图
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稍后更新