Retrofit+okHttp3一步一步封装MVP
2018-01-22 16:54
357 查看
Retrofit+okHttp3一步一步封装MVP
Retrofit+okhttp3 是目前最流行的网络请求框架,本文主要说明MVP一步步封装,对于Retrofit不会过多讲解。MVP算是目前比较火的一想移动端的代码架构,采用Presenter隔离View层和Model层,解耦View层由于涉及过多逻辑而造成的代码臃肿,导致调试以及后期更改麻烦,下面开始一步一步搭建Retrofit+okHttp3封装MVP:创建View基类接口,声明将来在View层的回调接口
public interface IMvpViewImpl<T extends CommonVo> { void requesting(); // 数据请求开始 void success(T data); // 请求成功 void fail(ErrorInfo errorInfo); // 请求失败 }
创建一个Presenter的抽象基类
/** * Author : luweicheng on 2018/1/19 0019 15:00 * E-mail :1769005961@qq.com * GitHub : https://github.com/luweicheng24 * funcation: presenter抽象基类 **/ public abstract class AbstactMvpPresenter<V extends IMvpViewImpl> { protected HashMap<String,String> paramMap; // 参数map private V mvpView; // 关联View public void attachView(V mvpView){ this.mvpView = mvpView; paramMap = new HashMap<>(); } // 与View接触绑定 public void detachView(){ this.mvpView = null; } // 获取View public V getMvpView() { return mvpView; } }
该类中的ParamMap是方便以后发送网络请求时的参数封装到map中,其中在Activity或者Fragment的创建时将Presenter与其关联,在界面销毁时设置传递到Presenter中的View为null,这样不会再Activity或者Fragment销毁时Presenter层仍然持有View的引用而导致内存泄漏。
创建抽象Activity实现View的接口,抽象一个创建Presenter的方法,方便子类实现各自的presenter,在onResume和onStop关联Presenter和去除关联。
/** * Author : luweicheng on 2018/1/19 0019 15:26 * E-mail :1769005961@qq.com * GitHub : https://github.com/luweicheng24 * funcation: Activity的抽象基类 **/ public abstract class AbstractMvpActivity<P extends AbstactMvpPresenter> extends AppCompatActivity implements IMvpViewImpl { protected P presenter; @Override protected void onResume() { super.onResume(); presenter = createPresenter(); if(presenter==null){ throw new RuntimeException("present is null"); } presenter.attachView(this) } @Override protected void onStop() { super.onStop(); if(presenter!=null){ presenter.detachView(); } } // 子类创建具体的Presenter protected abstract P createPresenter(); }
MVP 的基本基类就编写完成,下面看一下如何封装一个适合项目的网络请求
先编写一个简单的网络接口,改接口是临时本地编写的一个ssm的后台项目,Controller代码如下:
/** * Author : luweicheng on 2018/1/22 0022 10:05 * E-mail :1769005961@qq.com * GitHub : https://github.com/luweicheng24 * funcation: */ @Controller public class LoginController { @RequestMapping("/login") @ResponseBody() public Map<String,Object> login(@RequestParam(value = "name",required=false) String name, @RequestParam(value = "password",required=false) String password) { Map requestMap = new HashMap(); if (name != null && password != null ) { requestMap.put("res_code",1); requestMap.put("res_msg","登陆成功"); LoginParm parm = new LoginParm(); parm.setSex("男"); parm.setName(name); parm.setAge("23"); requestMap.put("user",parm); } else { requestMap.put("res_code",1); requestMap.put("res_msg","登陆成功"); } return requestMap; } }
以上代码只是为了测试方便随意编写的一个Controller,数据返回格式为json。
移动端的接口代码,编写个基于post和get的两种http请求方式:
/** * Author : luweicheng on 2018/1/19 0019 16:20 * E-mail :1769005961@qq.com * GitHub : https://github.com/luweicheng24 * funcation: Retrofit接口类 **/ public interface ApiService { // retrofit 的get请求 @GET("/login") Call<LoginVo> loginGet(@QueryMap HashMap<String,String> paramMap); // retrofit的post请求 @FormUrlEncoded @POST("/login") Call<LoginVo> loginPost(@FieldMap HashMap<String,String> paramMap); }
下面是编写ApiClient作为发起网络请求的客户端:
/** * Author : luweicheng on 2018/1/22 0022 09:41 * E-mail :1769005961@qq.com * GitHub : https://github.com/luweicheng24 * funcation: Api客户端 **/ public class ApiClient { private ApiClient(){} public static ApiClient client; private static ApiService apiService; static { Retrofit retrofit = new Retrofit .Builder() .baseUrl("http://192.168.1.35:8081/") .client(new OkHttpClient()) .addConverterFactory( GsonConverterFactory.create()) .build(); apiService = retrofit.create(ApiService.class); } public static ApiService getApiService(){ if(client==null){ synchronized (ApiClient.class){ if(client==null){ client = new ApiClient(); } } } return apiService; } }
这里先来看一下服务端返回的数据格式:
{"res_msg":"登陆成功", "user":{"name":"123","age":"23","sex":"男"}, "res_code":1 }
以上数据格式也是现在基本所有服务器返回数据的基本格式,所以针对这种数据格式先来创建一个CommonParm:
public class CommonVo{ private int res_code; private String res_msg; public int getRes_code() { return res_code; } public void setRes_code(int res_code) { this.res_code = res_code; } public String getRes_msg() { return res_msg; } public void setRes_msg(String res_msg) { this.res_msg = res_msg; } }
再来编写一个具体的vo类,继承自CommonParam
public class LoginVo extends CommonVo{ /** * user : {"name":"123","age":"23","sex":"男"} */ private UserBean user; public UserBean getUser() { return user; } public void setUser(UserBean user) { this.user = user; } public static class UserBean { /** * name : 123 * age : 23 * sex : 男 */ private String name; private String age; private String sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { return "UserBean{" + "name='" + name + '\'' + ", age='" + age + '\'' + ", sex='" + sex + '\'' + '}'; } } @Override public String toString() { return "LoginVo{" + "user=" + user + '}'; } }
由于服务器返回的数据根据res_code会有不同的操作,所以本地封装一个ErrorInfo类来封装code和msg:
public class ErrorInfo { private int errCode; private String errMsg; public ErrorInfo(int errCode, String errMsg) { this.errCode = errCode; this.errMsg = errMsg; } public int getErrCode() { return errCode; } public void setErrCode(int errCode) { this.errCode = errCode; } public String getErrMsg() { return errMsg; } public void setErrMsg(String errMsg) { this.errMsg = errMsg; } @Override public String toString() { return "ErrorInfo{" + "errCode=" + errCode + ", errMsg='" + errMsg + '\'' + '}'; } }
在完成上面的编写后,就到了编写一个具体mvp流程了,如何进行一次封装的网络请求:
public class LoginModel { // 登录 public void login(HashMap<String,String> paraMap, HttpCallBack customCallBack){ Call<LoginVo> call = ApiClient.getApiService().loginGet(paraMap); call.enqueue(customCallBack); } }
HttpCallBack 是一个自定义的回调处理,实现了Retrofit的CallBack接口,在内部根据服务器返回的信息进行处理:
public class HttpCallBack<T extends CommonVo> implements Callback { public static final int NET_ERROR = 10086; public HttpCallBack(ResponseResultListener resultListener) { this.resultListener = resultListener; } @Override public void onResponse(Call call, Response response) { T vo = (T) response.body(); // 根据code过滤数据 if (resultListener != null&&vo.getRes_code()==1) { resultListener.success(vo); } else { resultListener.error(new ErrorInfo(vo.getRes_code(), vo.getRes_msg())); } } @Override public void onFailure(Call call, Throwable t) { resultListener.error(new ErrorInfo(NET_ERROR, "网络异常")); Log.e("net-error", "onFailure: " + t.getMessage()); } private ResponseResultListener resultListener; public void setResultListener(ResponseResultListener resultListener) { this.resultListener = resultListener; } }
在Callback的两个回调接口中需要将该处的数据传递出去,所以利用一个接口回调将数据传递出去:
public interface ResponseResultListener<T extends CommonVo> { // 正确返回数据(res_code!=1) void success(T t); // 返回数据有错 void error(ErrorInfo errorInfo); }
接下来编写LoginActiivty:
public class LoginActivity extends AbstractMvpActivity<LoginPresenter> { private EditText et_name, et_password; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); et_name = (EditText) findViewById(R.id.et_name); et_password = (EditText) findViewById(R.id.et_passsword); } @Override protected LoginPresenter createPresenter() { return new LoginPresenter(); } public void login(View view) { if (TextUtils.isEmpty(et_name.getText()) && TextUtils.isEmpty(et_password.getText())) { presenter.requestLogin(et_name.getText().toString(), et_password.getText().toString()); } else { Toast.makeText(this, "用户名或者密码为空", Toast.LENGTH_SHORT).show(); } } @Override public void requesting() { System.out.println("数据请求中"); } @Override public void success(CommonVo data) { ((LoginVo) data).toString(); Toast.makeText(this, "登录成功"+((LoginVo) data).toString(), Toast.LENGTH_SHORT).show(); System.out.println("loginactivity" + ((LoginVo) data).toString()); } @Override public void fail(ErrorInfo errorInfo) { Toast.makeText(this, "登录失败"+errorInfo.toString(), Toast.LENGTH_SHORT).show(); System.out.println("loginactivity" + errorInfo.toString()); } }
该类只是继承AbstractMvpActivity规定Presenter的泛型,实现如下方法:
@Override protected LoginPresenter createPresenter() { return new LoginPresenter(); }
接下里就是具体的Presenter类的编写了:
public class LoginPresenter extends AbstactMvpPresenter<IMvpViewImpl> { private IMvpViewImpl view; private LoginModel loginModel; public LoginPresenter(){ loginModel = new LoginModel(); } // 请求登录 public void requestLogin(String name,String password){ view = getMvpView(); if(view != null){ view.requesting(); } paramMap.put("name",name); paramMap.put("password",password); loginModel.login(paramMap,new HttpCallBack(new ResponseResultListener<LoginVo>() { @Override public void success(LoginVo loginVo) { view.success(loginVo); } @Override public void error(ErrorInfo errorInfo) { view.fail(errorInfo); System.out.println(errorInfo.toString()); } })); } }
Loginpresenter在创建的时候便会创建一个LoginModel,当View层调用Presenter时,Presenter调用Model层的数据请求,数据请求完成后将数据回调给View层。
Model层只做数据的存取操作:
public class LoginModel { // 登录 public void login(HashMap<String,String> paraMap, HttpCallBack customCallBack){ Call<LoginVo> call = ApiClient.getApiService().loginGet(paraMap); call.enqueue(customCallBack); } }
这样一个采用Retrofit+okhttp3搭建完整的MVP架构就算完成了,其中还有还需很大的优化,github源码下载,欢迎issue,star。
相关文章推荐
- OKhttp+Retrofit的封装加单例模式MVP实现
- retrofit+mvp+okhttp+rxjava封装
- 单列模式,RecyclerView适配器封装,BroadcastReceiver介绍,Mvp+Retrofit+rxjava+okhttp框架的梳理总结
- 使用MVP注册登录模块+封装的OKhttp,拦截器+QQ第三方登录+RecyclerView+SpringView上拉加载下拉刷新网络数据
- MVP模式+OKhttp的封装请求数据
- 实战MVP请求数据OKHttp封装RecyclerView上拉刷新下拉加载
- Android——MVP架构OkHttp的二次封装以及RecyclerView的使用
- Retrofit+MVP框架封装记录篇
- MVP和Retrofit+Rxjava+OkHttp封装结合请求数据
- 网络请求Okhttp封装加单例加拦截器 结合MVP
- 基于OKhttp的MVP封装
- MVP和retrofit+OKhttp图片请求
- 终极封装 Rxjava+Retrofit+okhttp+mvp实现
- RxJava+okhttp+Retrofit+Mvp 的封装
- 仿京东APP分类页面(mvp模式+OkHttp封装工具类+拦截器+弱引用回收)
- 基于MVP架构的OKHttp3的封装
- MVP实现购物车(二级列表),删除结算功能,拦截器+封装okHttp
- OKHttp网络请求封装
- MVP实战心得(三)---封装Retrofit2.0+RxAndroid+RxBus
- **张鸿洋**封装的okhttputils 上传文字加图片 实现发表朋友圈效果