网络(三):MVP+RxJava2+Retrofit2+OkHttp3
参考:
https://www.jianshu.com/p/0ad99e598dba
https://www.jianshu.com/p/df4eee78085c
文章分析重新优化,先给个下载链接
github地址:https://github.com/LPTim/MVP-Retrofit2-okhttp3-Rxjava2
csdn地址:https://download.csdn.net/download/loocanp/11229241
相关业务需求及解决方案
1、Retrofit配置及各情况处理
2、Retrofit,Gson解析,自定义解析内容(如code=1全部解析,code=0不做解析)
3、Retrofit,Gson解析,请求返回的类型不统一,假如double返回的是null
4、Retrofit实现cookie自动化管理
5、Retrofit文件上传
6、Retrofit文件下载(稍等给连接)
代码结构如下
最终实现效果如下图
//网络请求
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基类代码实现,对日志参数进行了拦截
注:get请求参数打印会拼接在url之后,post打印单独显示
打印框架 logger,日志结构很直观(推荐使用)
/**
* 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();
// analyzeJson("data", "", content);
Logger.wtf(TAG, "----------Request Start----------------");
printParams(request.body());
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();
}
};
/**
* 请求参数日志打印
*
* @param body
*/
private void printParams(RequestBody body) {
if (body != null) {
Buffer buffer = new Buffer();
try {
body.writeTo(buffer);
Charset charset = Charset.forName("UTF-8");
MediaType contentType = body.contentType();
if (contentType != null) {
charset = contentType.charset(UTF_8);
}
String params = buffer.readString(charset);
Logger.e(TAG, "请求参数: | " + params);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public interface ApiServer {
//示例 多种类型请求方式
// @POST("api/Activity/get_activities?")
// Observable>> getApi1(@Query("time") String requestType);
// @GET("api/Activity/get_activities?")
// Observable>> getApi1(@Query("time") String requestType);
// @FormUrlEncoded
// @POST("api/Activity/get_activities?")
// Observable>> getApi1(@Field("time") String requestType);
// @FormUrlEncoded
// @POST("api/Activity/get_activities?")
// Observable>> getApi1(@FieldMap HashMap params);
// @Multipart
// @POST("api/Activity/get_activities?")
// Observable>> getApi1(@PartMap Map map);
@POST("api/Activity/get_activities?")
Observable>> getMain(@Query("time") String requestType);
}
/**
* 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);
}
/**
* File descripition: 创建Presenter基类
*
* @author lp
* @date 2018/6/19
*/
public class BasePresenter {
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();
}
}
}
import java.io.Serializable;
/**
* File descripition: mode基类
*
* @author lp
* @date 2018/6/19
*/
public class BaseModel 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 +
'}';
}
}
1、onStart方法所处理业务,网络开始请求,我们需要显示一个dialog来让用户感受到数据正在请求,所以,onStart回调我们在BaseView中定义的方法showLoading(),所以
@Override
protected void onStart() {
if (view != null) {
view.showLoading();
}
}
2、onNext方法所处理业务,执行到这里,证明网络已经请求成功,这是我们可以让刚才显示的dialog消失,也可以将回调消失的方法放到onComplete中,onNext(T o) 其中o是服务器请求下来的数据,我们开始解析判断BaseModel model = (BaseModel) o;强转成我们需要的类型,有人说强转是不是不好,其实字段名和类型我们已经明确,并且不会有误差,只强转俩个字段code和msg,性能的开销可以忽略不计,属于多态的特性,如果不想强转,Gson解析也是可以的,我们的目的是拿到code值,来判断请求是否成功,如果等于1(假如1是正常请求)将数据赋予abstract void onSuccess(T o),当我们new BaseObserver时候需要重写onSuccess就能拿到对应数据了,如果不是1就回掉BaseView中定义的方法onErrorCode(model ),并将BaseBean给它,这样对应的activity或者fragment也可以拿到code值做相对于的操作
@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());
}
}
3、onError方法所处理业务,执行到这个方法,也就是说网络请求失败了,没有拿到服务器返回的值,所以我们需要判断为何失败,是什么异常
@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("未知错误");
}
}
}
4、onComplete方法所处理业务,代表业务处理完毕,所以说dialog消失写到这里也是可以的
/**
* File descripition: 数据处理基类
*
* @author lp
* @date 2018/6/19
*/
public abstract class BaseObserver extends DisposableObserver {
/**
* 于服务器约定 返回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);
}
**
**
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基类
*
*
* @author lp
* @date 2018/5/16
*/
public abstract class BaseActivity
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();
}
}
}
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.lp.mvp_network.base.mvp.BaseModel;
import com.lp.mvp_network.base.mvp.BasePresenter;
import com.lp.mvp_network.base.mvp.BaseView;
/**
* File descripition: ftagment 基类
*
* @author lp
* @date 2018/6/19
*/
public abstract class BaseFragment extends Fragment implements BaseView {
public View view;
public Context mContext;
protected P mPresenter;
protected abstract P createPresenter();
//错误提示框 警告框 成功提示框
public PromptDialog promptDialog;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
view = inflater.inflate(getLayoutId(), container, false);
mContext = getActivity();
mPresenter = createPresenter();
this.initToolbar(savedInstanceState);
this.initData();
return view;
}
/**
* 获取布局ID
*
* @return
*/
protected abstract int getLayoutId();
/**
* 处理顶部title
*
* @param savedInstanceState
*/
protected abstract void initToolbar(Bundle savedInstanceState);
/**
* 数据初始化操作
*/
protected abstract void initData();
public void showToast(String str) {
}
public void showLongToast(String str) {
}
@Override
public void showError(String msg) {
showToast(msg);
}
@Override
public void onErrorCode(BaseModel model) {
}
@Override
public void showLoading() {
// showLoadingDialog();
}
@Override
public void hideLoading() {
closeLoadingDialog();
}
public void closeLoadingDialog() {
if (mLodingDialog != null && mLodingDialog.isShowing()) {
mLodingDialog.dismiss();
}
}
/**
* 加载中...
*/
public void showLoadingDialog() {
if (promptDialog == null) {
promptDialog = new PromptDialog(this);
}
promptDialog.showLoading("加载中...",false);
}
@Override
public void onDestroy() {
super.onDestroy();
this.view = null;
if (mPresenter != null) {
mPresenter.detachView();
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
}
}
以上内容为MVP+Retrofit2+okhttp3+Rxjava2全部封装,其间有些地方需根据自己项目内容所做修改,下边为大家演示下如何在对应activity请求数据
比如实体类如下
/**
* 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;
}
}
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> o);
}
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 {
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>) o);
}
@Override
public void onError(String msg) {
if (baseView != null) {
baseView.showError(msg);
}
}
});
}
}
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 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> o) {
//数据返回
tv_msg.setText(o.getData().toString());
}
@Override
public void onClick(View v) {
//数据请求
mPresenter.commentAdd();
}
}
答:BaseActivity和BaseFragment中都有这俩个方法
//显示加载进度框回调
@Override
public void showLoading() {
showLoadingDialog();
}
//隐藏进度框回调
@Override
public void hideLoading() {
closeLoadingDialog();
}
如果说我本页面都不想显示Loading动画,那就在对应的Activity重写下父类的方法,比如
@Override
public void showLoading() {
// super.showLoading(); //将super去掉 就不会显示Loading动画了
}
如果我们需要显示就在对应的Fragment调用请求方法之后手动掉一下父类的显示Loading方法,如下:
mPresenter.collectApi("id");
showLoadingDialog();
答:可以在BaseActivity判断跳页面
//BaseActivity代码
@Override
public void onErrorCode(BaseModel model) {
if (model.getErrcode() == 1001) {
startLogin();
}
}
private void startLogin() {
startActivity(LoginActivity.class);
}
如果想在对应Activity操作,那就在对应Activity重写此方法
//对应Activity代码
@Override
public void onErrorCode(BaseModel model) {
//super.onErrorCode(model);
if (model.getErrcode()==1001){
//............................................
}else if (model.getErrcode()==1002){
//............................................
}
}
MVP+Retrofit2+okhttp3+Rxjava2网络请求封装完成
github地址:https://github.com/LPTim/MVP-Retrofit2-okhttp3-Rxjava2
csdn地址:https://download.csdn.net/download/loocanp/11229241