本章节讲述MVP模式实现Okhttp框架
MVP作为MVC的演化,解决了MVC不少的缺点,对于Android来说,MVP的model层相对于MVC是一样的,而activity和fragment不再是controller层,而是纯粹的view层,所有关于用户事件的转发全部交由presenter层处理。下面还是让我们看图
从图中就可以看出,最明显的差别就是view层和model层不再相互可知,完全的解耦,取而代之的presenter层充当了桥梁的作用,用于操作view层发出的事件传递到presenter层中,presenter层去操作model层,并且将数据返回给view层,整个过程中view层和model层完全没有联系。看到这里大家可能会问,虽然view层和model层解耦了,但是view层和presenter层不是耦合在一起了吗?其实不是的,对于view层和presenter层的通信,我们是可以通过接口实现的。
具体的意思就是说我们的activity,fragment可以去实现实现定义好的接口,而在对应的presenter中通过接口调用方法。不仅如此,我们还可以编写测试用的View,模拟用户的各种操作,从而实现对Presenter的测试。这就解决了MVC模式中测试,维护难的问题。
当然,其实最好的方式是使用fragment作为view层,而activity则是用于创建view层(fragment)和presenter层(presenter)的一个控制器。
和MVC最大的不同,MVP把activity作为了view层,通过代码也可以看到,整个activity没有任何和model层相关的逻辑代码,取而代之的是把代码放到了presenter层中,presenter获取了model层的数据之后,通过接口的形式将view层需要的数据返回给它就OK了。
这样的好处是。首先,activity的代码逻辑减少了,其次,view层和model层完全解耦,具体来说,如果你需要测试一个http请求是否顺利,你不需要写一个activity,只需要写一个java类,实现对应的接口,presenter获取了数据自然会调用相应的方法,相应的,你也可以自己在presenter中mock数据,分发给view层,用来测试布局是否正确。
详解:Android MVC MVP MVVM 模式
二.OKHttp+MVP
项目截图
以登录为例
M层代码 Model
1.常量 constant
设置 OKHttp网络超时时间 各个接口Url等等。
2.OkHttpClient工具类
/**
* Created by wjn on 2017/11/22.
* OkHttp框架 封装方法
*/
public class RxAndroidOkhttp {
private OkHttpClient mOkHttpClient = null;//OkHttpClient 对象
private static RxAndroidOkhttp mRxAndroidOkhttp = null;//RxAndroidOkhttp 对象
/**
* 构造方法私有化
*/
private RxAndroidOkhttp() {
//创建okhttp对象 以及连接,读,取超时时间
mOkHttpClient = new OkHttpClient.Builder()
.connectTimeout(DataConstant.nettimeout, TimeUnit.SECONDS)//连接时间
.readTimeout(DataConstant.nettimeout, TimeUnit.SECONDS)//读时间
.writeTimeout(DataConstant.nettimeout, TimeUnit.SECONDS)//写时间
.build();
}
/**
* 获取此单例类对象的方法
*/
public static RxAndroidOkhttp getInstance() {
if (null == mRxAndroidOkhttp) {//单例对象为空
synchronized (RxAndroidOkhttp.class) {
mRxAndroidOkhttp = new RxAndroidOkhttp();
}
}
return mRxAndroidOkhttp;
}
/**
* get请求方法
*/
public Observable get(final String url) {
//创建被观察者
Observable observable = Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber super String> subscriber) {
final Subscriber mSubscriber = subscriber;
//没有取消订阅的时候
if (!mSubscriber.isUnsubscribed()) {
//get请求
Request request = null;
String cookie = MyApplication.getOkhttpCookie();
if (!BooleanUtils.isEmpty(cookie)) {
request = new Request.Builder()
.addHeader("cookie", cookie)
.url(url)
.get()
.build();
} else {
request = new Request.Builder()
.url(url)
.get()
.build();
}
if (null != mOkHttpClient) {
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//通知订阅者的错误信息
mSubscriber.onError(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (null != response) {//response 不为空
if (response.isSuccessful()) {//response 请求成功
Headers headers = response.headers();
List cookies = headers.values("Set-Cookie");
if (cookies.size() > 0) {
String session = cookies.get(0);
String result = session.substring(0, session.indexOf(";"));
MyApplication.setOkhttpCookie(result);
}
//通知订阅者的成功信息
mSubscriber.onNext(response.body().string());
} else {//response 请求失败
//通知订阅者的错误信息
IOException IOExceptionx = new IOException();
mSubscriber.onError(IOExceptionx);
}
} else {//response 为空
//通知订阅者的错误信息
IOException IOExceptionx = new IOException();
mSubscriber.onError(IOExceptionx);
}
//通知完毕
mSubscriber.onCompleted();
}
});
}
}
}
});
return observable;
}
/**
* post请求方法
*/
public Observable post(final String url, final Map params) {
//创建被观察者
Observable observable = Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber super String> subscriber) {
final Subscriber mSubscriber = subscriber;
//没有取消订阅的时候
if (!mSubscriber.isUnsubscribed()) {
//获取FormBody.Builder 对象
FormBody.Builder builder = new FormBody.Builder();
//遍历params 将其放入到FormBody.Builder 对象
if (params != null && !params.isEmpty()) {
for (Map.Entry entry : params.entrySet()) {
builder.add(entry.getKey(), entry.getValue());
}
}
//获取RequestBody 对象
RequestBody requestBody = builder.build();
//构建post请求
Request request = null;
String cookie = MyApplication.getOkhttpCookie();
if (!BooleanUtils.isEmpty(cookie)) {
request = new Request.Builder()
.addHeader("cookie", cookie)
.url(url)
.post(requestBody)
.build();
} else {
request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
}
if (mOkHttpClient != null) {
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//通知订阅者的错误信息
mSubscriber.onError(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (null != response) {//response 不为空
if (response.isSuccessful()) {//response 请求成功
Headers headers = response.headers();
List cookies = headers.values("Set-Cookie");
if (cookies.size() > 0) {
String session = cookies.get(0);
String result = session.substring(0, session.indexOf(";"));
MyApplication.setOkhttpCookie(result);
}
//通知订阅者的成功信息
mSubscriber.onNext(response.body().string());
} else {//response 请求失败
//通知订阅者的错误信息
IOException IOExceptionx = new IOException();
mSubscriber.onError(IOExceptionx);
}
} else {//response 为空
//通知订阅者的错误信息
IOException IOExceptionx = new IOException();
mSubscriber.onError(IOExceptionx);
}
//通知完毕
mSubscriber.onCompleted();
}
});
}
}
}
});
return observable;
}
/**
* 自定义onError字符串(欢迎页面)
*/
public static String getonErrorSplashString(Throwable e) {
String result = StringConstant.Splashing3;
if (null != e) {
Throwable throwable = e.getCause();
if (null != throwable) {
if (throwable.equals(SocketTimeoutException.class)) {
result = StringConstant.Splashing5;
}
}
String msg = e.getMessage();
if (!BooleanUtils.isEmpty(msg) && msg.contains("timeout")) {
result = StringConstant.Splashing5;
}
}
return result;
}
/**
* 自定义onError字符串(其他页面)
*/
public static String getonErrorOtherString(Throwable e) {
String result = StringConstant.errorstate1;
if (null != e) {
Throwable throwable = e.getCause();
if (null != throwable) {
if (throwable.equals(SocketTimeoutException.class)) {
result = StringConstant.errorstate4;
}
}
String msg = e.getMessage();
if (!BooleanUtils.isEmpty(msg) && msg.contains("timeout")) {
result = StringConstant.errorstate4;
}
}
return result;
}
}
P层 Presenter
1.基础Presenter
public class BasePresenter {
protected RxAndroidOkhttp mRxAndroidOkhttp=null;//RxAndroidOkhttp对象
protected Observable mObservable=null;//get post 方式请求的Observable对象
public BasePresenter(){
mRxAndroidOkhttp=RxAndroidOkhttp.getInstance();
}
}
2.登录Presenter
public class LoginPresenter extends BasePresenter{
/**
* 构造方法
* */
public LoginPresenter(){
super();
}
/**
* 响应回调
* */
private OkHttpResponseInterface mOkHttpResponseInterface=null;
public void setOnOkHttpResponseListener(OkHttpResponseInterface okHttpResponseInterface){
mOkHttpResponseInterface=okHttpResponseInterface;
}
/**
* 请求数据
* */
public void requestService(final String type,final String posturl,final Map params){
if(null!=mRxAndroidOkhttp){
//observable定义被观察者
mObservable=mRxAndroidOkhttp.post(posturl,params);
if(null!=mObservable){
//定义观察者
Subscriber mSubscriber=new Subscriber(){
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
if(mOkHttpResponseInterface!=null) {
mOkHttpResponseInterface.onOkHttpError(type,e);
}
}
@Override
public void onNext(String s) {
if(mOkHttpResponseInterface!=null) {
mOkHttpResponseInterface.onOkHttpNext(type,s);
}
}
};
/**
* 订阅者关联被观察者
* Schedulers.io()说明是输入输出的计划任务
* AndroidSchedulers.mainThread()说明订阅者是中ui主线程中执行
* */
mObservable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(mSubscriber);
}
}
}
}
接口
/**
* @param :OkHttpResponse接口
* @author :wjn
* */
public interface OkHttpResponseInterface {
void onOkHttpError(String type,Throwable e);
void onOkHttpNext(String type,String s);
}
V层 View
1.基础Activity
public abstract class BaseActivity extends AppCompatActivity {
/**
* onCreate 方法
* */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
setStatusBarMode();
initView();
initPresenter();
}
/**
* 获取布局id
*/
public abstract int getLayoutId();
/**
* 初始化视图
*/
protected abstract void initView();
/**
* 初始化视图
*/
protected abstract void initPresenter();
/**
* 初始化状态栏
* 文字、图标是否为黑色 (false为默认的白色)
*/
protected abstract boolean isTextDark();
/**
* 初始化状态栏
* 本地颜色还是获取的rgb颜色值
*/
protected abstract boolean islocalcolor();
/**
* 初始化状态栏
* 状态栏颜色
*/
protected abstract int colorId();
/**
* 初始化状态栏
*/
protected void setStatusBarMode(){
//根据状态栏颜色来决定 状态栏背景 用黑色还是白色 true:是否修改状态栏字体颜色
StatusBarUtil.setStatusBarMode(this, isTextDark(), islocalcolor(), colorId());
}
}
2.登录Activity
public class MVP_LoginActivity extends BaseActivity implements OkHttpResponseInterface, View.OnClickListener {
private TextView textView1;
private TextView textView11;
private TextView resultTextView;
private LoginPresenter loginPresenter=null;
/**
* 获取布局
* */
@Override
public int getLayoutId() {
return R.layout.activity_login;
}
/**
* 初始化状态栏
* 文字、图标是否为黑色 (false为默认的白色)
*/
@Override
protected boolean isTextDark() {
return true;
}
/**
* 初始化状态栏
* 本地颜色还是获取的rgb颜色值
*/
@Override
protected boolean islocalcolor() {
return false;
}
/**
* 初始化状态栏
* 状态栏颜色
*/
@Override
protected int colorId() {
return R.color.baise;
}
/**
* 初始化View方法
* */
@Override
protected void initView() {
textView1=findViewById(R.id.activity_login_textview1);
textView11=findViewById(R.id.activity_login_textview11);
resultTextView=findViewById(R.id.activity_login_textview2);
textView1.setOnClickListener(this);
textView11.setOnClickListener(this);
}
/**
* 初始化Presenter
* */
@Override
protected void initPresenter() {
loginPresenter=new LoginPresenter();
loginPresenter.setOnOkHttpResponseListener(this);
}
/**
* 各种点击事件的方法
* */
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.activity_login_textview1://登录 Post
loginMethod();
break;
case R.id.activity_login_textview11://翻译 Post
fanyiMethod();
break;
default:
break;
}
}
/**
* 自定义 OkHttp onError方法
* */
@Override
public void onOkHttpError(String type, Throwable e) {
resultTextView.setText(e.getMessage());
}
/**
* 自定义 OkHttp onNext方法
* */
@Override
public void onOkHttpNext(String type, String s) {
if("Login".equals(type)){
resultTextView.setText("登录结果显示:\n\n"+s);
}else if("fanyi".equals(type)){
resultTextView.setText("翻译结果显示:\n\n"+s);
}
}
/**
* 登录方法
* */
private void loginMethod(){
resultTextView.setText("登录数据加载中...");
Map params = new HashMap<>();
params.put("userName", "");//用户名
params.put("password", "");//密码
params.put("JPID", "");//JPID 手机型号
params.put("token", "");//登录token
loginPresenter.requestService("Login",StringConstant.loginposturl,params);
}
/**
* 翻译方法
* */
private void fanyiMethod(){
resultTextView.setText("翻译数据加载中...");
Map params = new HashMap<>();
params.put("keyfrom", "");
params.put("key", "");
params.put("type", "");
params.put("doctype", "");
params.put("version", "");
params.put("q", "");
loginPresenter.requestService("fanyi",StringConstant.fanyiposturl,params);
}
}
3.登录Activity布局