前面我写过MVP模式,但是过于浅显,今天我在来详细的探究下该模式
1、什么是MVP?
通俗讲解:就是通过Presenter将View和Mode解耦
M(模型)->Model(模型)包括:与数据相关都属于M层(例如:数据库、文件、网络、数据解析、数据保存等等......)bean也属于M层,但M层不单单指M层
V->View包括:在MVC中View只是一个单纯视图,但是在MVP中(例如:Activity、Fragment、布局),即:与UI相关都属于V层
P->Presenter包括:调度,通过P层将我们的View层和Model进行关联转换,将M转换为V,同时使M和V解耦
2、MVP和Java设计模式有什么区别?
举例说明:北京鸟巢(整体架构)---MVP---范围大而广
窗户设计、场地设计、跑到设计、座位设计等等......---相当于设计模式---针对具体的问题或者说场景提出不同解决方案
大家思考下,如果你连鸟巢整体架构都没有,那你单单弄一个窗户设计有意义吗?
3、MVP架构交互流程?
4、案例分析?---登录为例
看下图片流程
我们看下使用MVP如何实现一个简单的网络登录
首先看下各个类名
三个Utils类,这三个类实现了网络的请求,当然你完全可以换成OKHttp请求或者Retrofit
package com.example.acer.myfirstmvp.utils;
import android.os.AsyncTask;
public class HttpTask extends AsyncTask<String, Void, String> {
private HttpUtils.OnHttpResultListener onHttpResultListener;
public HttpTask(HttpUtils.OnHttpResultListener onHttpResultListener) {
this.onHttpResultListener = onHttpResultListener;
}
@Override
protected String doInBackground(String... params) {
try {
return HttpUtils.post(params[0], params[1], params[2]);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(String result) {
if (this.onHttpResultListener != null) {
this.onHttpResultListener.onResult(result);
}
}
}
package com.example.acer.myfirstmvp.utils;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpUtils {
public static final String URL_STR = "http://192.168.57.1:8080/Dream_4_23_PhoneGapServer/PhoneGapServlet";
public static String get(String urlStr) {
String result = null;
try {
URL url = new URL(urlStr);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.setReadTimeout(5000);
connection.setRequestMethod("GET");
connection.setDoInput(true);
if (connection.getResponseCode() == 200) {
InputStream inStream = connection.getInputStream();
result = new String(StreamTool.readInputStream(inStream));
return result;
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public static String post(String urlStr, String username, String password)
throws Exception {
StringBuffer sb = null;
String param = "username=" + username + "&password=" + password;
URL url = new URL(urlStr);
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
// 设置参数
httpConn.setDoOutput(true); // 需要输出
httpConn.setDoInput(true); // 需要输入
httpConn.setUseCaches(false); // 不允许缓存
httpConn.setRequestMethod("POST"); // 设置POST方式连接
// 设置请求属性
httpConn.setRequestProperty("Charset", "UTF-8");
// 连接,也可以不用明文connect,使用下面的httpConn.getOutputStream()会自动connect
httpConn.connect();
// 建立输入流,向指向的URL传入参数
DataOutputStream dos = new DataOutputStream(httpConn.getOutputStream());
dos.writeBytes(param.toString());
dos.flush();
dos.close();
// 获得响应状态
int resultCode = httpConn.getResponseCode();
sb = new StringBuffer();
if (HttpURLConnection.HTTP_OK == resultCode) {
String readLine = new String();
//解析网络请求数据
BufferedReader responseReader = new BufferedReader(
new InputStreamReader(httpConn.getInputStream(), "UTF-8"));
while ((readLine = responseReader.readLine()) != null) {
sb.append(readLine).append("\n");
}
responseReader.close();
return sb.toString();
}
return null;
}
public interface OnHttpResultListener {
public void onResult(String result);
}
}
package com.example.acer.myfirstmvp.utils;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
public class StreamTool {
/**
* 从输入流中读取数据
* @param inStream
* @return
* @throws Exception
*/
public static byte[] readInputStream(InputStream inStream) throws Exception{
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while( (len = inStream.read(buffer)) !=-1 ){
outStream.write(buffer, 0, len);
}
byte[] data = outStream.toByteArray();//网页的二进制数据
outStream.close();
inStream.close();
return data;
}
}
三个没用的类贴完了,下面我们来贴出我们的MVP相关的类,在代码中都做注释了,这里就不废话了,代码很简单;
M类,
package com.example.acer.myfirstmvp.simple2;
import com.example.acer.myfirstmvp.utils.HttpTask;
import com.example.acer.myfirstmvp.utils.HttpUtils;
/**
* M层(数据网络都在这一层)
*/
public class MainModel {
public void login(String username, String pwd, final HttpUtils.OnHttpResultListener onHttpResultListener){
HttpTask httpTask=new HttpTask(new HttpUtils.OnHttpResultListener() {
@Override
public void onResult(String result) {
//解析数据
//更新UI
onHttpResultListener.onResult(result);
}
});
httpTask.execute(username,pwd,"http://www.baidu.com");
}
}
V类,首先要有一个接口
package com.example.acer.myfirstmvp.simple2;
/**
* View接口,通过View暴露接口去访问
*/
public interface MainView {
public void onLoginResult(String result);
}
package com.example.acer.myfirstmvp.simple2;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Toast;
import com.example.acer.myfirstmvp.R;
/**
* MVPP将View(本例子中的Activity)和相关操作进行了完全的隔离,将来你修改网络请求方式(比如修改成OKhttp),丝毫不影响UI,UI只需要
* 通过onLoginResult拿到该结果;更加方便团队开发,大大提高了开发效率
*/
public class MainActivity2 extends AppCompatActivity implements MainView{
private MainPresenter mMainPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
mMainPresenter=new MainPresenter(this);
}
public void click(View v){
this.mMainPresenter.login("Dream","123");
}
@Override
public void onLoginResult(String result) {
//更新UI
Toast.makeText(this,result,Toast.LENGTH_LONG).show();
}
}
p类,关键的类
package com.example.acer.myfirstmvp.simple2;
import com.example.acer.myfirstmvp.utils.HttpUtils;
/**
* P层,对M和V进行关联
*/
public class MainPresenter {
private MainView mainView;
private MainModel maiModel;
public MainPresenter(MainView mainView){
this.maiModel=new MainModel();
this.mainView=mainView;
}
public void login(String username,String pwd){
this.maiModel.login(username, pwd, new HttpUtils.OnHttpResultListener() {
@Override
public void onResult(String result) {
mainView.onLoginResult(result);
}
});
}
}
当然这个例子太简单,实际开发中没毛线用;举个两个例子
问题一:
假设:Activity意外关闭,这个时候网络请求还在进行,当数据返回的时候,发现Activity挂掉了?(或者说Fragment)---挂掉了你还要回调,这可是一个垃圾操作啊
该怎么办?---解决办法:提供一个销毁的方法
大家看下我们的解决方案
首先修改下P
package com.example.acer.myfirstmvp.simple3;
import com.example.acer.myfirstmvp.utils.HttpUtils;
/**
* P层,对M和V进行关联
*/
public class MainPresenter {
private MainView mainView;
private MainModel maiModel;
// public MainPresenter(MainView mainView){
// this.maiModel=new MainModel();
// this.mainView=mainView;
// }
//改动的地方
**public MainPresenter() {
//改动的地方
this.maiModel = new MainModel();
}**
public void login(String username, String pwd) {
this.maiModel.login(username, pwd, new HttpUtils.OnHttpResultListener() {
@Override
public void onResult(String result) {
if (mainView != null) {
mainView.onLoginResult(result);
}
}
});
}
**//改动的地方
//绑定视图层
public void attachView(MainView mainView) {
this.mainView = mainView;
}
//改动的地方
//解除绑定
public void detachView() {
this.mainView = null;
}**
}
P增加了两个方法(绑定和解除绑定的方法),同时修改下M层
package com.example.acer.myfirstmvp.simple3;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Toast;
import com.example.acer.myfirstmvp.R;
/**
* MVPP将View(本例子中的Activity)和相关操作进行了完全的隔离,将来你修改网络请求方式(比如修改成OKhttp),丝毫不影响UI,UI只需要
* 通过onLoginResult拿到该结果;更加方便团队开发,大大提高了开发效率
*/
public class MainActivity2 extends AppCompatActivity implements MainView {
private MainPresenter mMainPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
**//修改的地方
// mMainPresenter=new MainPresenter(this);
mMainPresenter=new MainPresenter();
this.mMainPresenter.attachView(this);**
}
public void click(View v){
this.mMainPresenter.login("Dream","123");
}
@Override
public void onLoginResult(String result) {
//更新UI
Toast.makeText(this,result,Toast.LENGTH_LONG).show();
}
**//修改的地方
@Override
protected void onDestroy() {
super.onDestroy();
this.mMainPresenter.detachView();
}**
}
但我们这样依然不完美,举个例子
项目开发当中Activity数量,或者说Fragment数量很庞大,这样会有很多代码冗余
该怎么办?---解决方案:单独抽象出来(引出抽象类)
下面我们看下,抽象类如何设计MVP
先看下类的结构
package com.tz.architect.mvp.simple4;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import com.tz.architect.mvp.R;
import com.tz.architect.mvp.simple4.framework.impl.MainPresenter;
import com.tz.architect.mvp.simple4.framework.impl.MainView;
import com.tz.architect.mvp.simple4.framework.impl.MvpActivity;
public class MainActivity extends MvpActivity<MainView, MainPresenter> implements MainView {
@Override
public MainPresenter bindPresenter() {
return new MainPresenter();
}
@Override
public MainView bindView() {
return this;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
// 其实在Android当中,本身就是一个非常典型MVC架构
// 在Android MVC中
// M代表:数据
// C代表: activity或者Fragment
// V代表:视图
// MVP适合大项目
// MVP更加便于团队开发
public void click(View v) {
getPresenter().login("Dream", "123456");
}
@Override
public void onLoginResult(String result) {
// 更新UI
Toast.makeText(this, result, Toast.LENGTH_LONG).show();
}
}
package com.tz.architect.mvp.simple4.framework;
public abstract class AbsMvpPresenter<V extends IMvpView> {
private V view;
public V getView() {
return view;
}
/**
* 绑定
*
* @param view
*/
public void attachView(V view) {
this.view = view;
}
/**
* 解除绑定
*/
public void detachView() {
this.view = null;
}
}
package com.tz.architect.mvp.simple4.framework;
public interface IMvpView {
}
package com.tz.architect.mvp.simple4.framework.impl;
import com.tz.architect.mvp.utils.HttpTask;
import com.tz.architect.mvp.utils.HttpUtils.OnHttpResultListener;
/**
* M层(数据、网络)
* @author Dream
*
*/
public class MainModel {
public void login(String username,String pwd,final OnHttpResultListener onHttpResultListener){
HttpTask httpTask = new HttpTask(new OnHttpResultListener() {
@Override
public void onResult(String result) {
onHttpResultListener.onResult(result);
}
});
httpTask.execute(username,pwd,"http://www.baidu.com");
}
}
package com.tz.architect.mvp.simple4.framework.impl;
import com.tz.architect.mvp.simple4.framework.AbsMvpPresenter;
import com.tz.architect.mvp.utils.HttpUtils.OnHttpResultListener;
/**
* P层
*
* @author Dream
*
*/
public class MainPresenter extends AbsMvpPresenter<MainView> {
private MainModel mainModel;
public MainPresenter() {
this.mainModel = new MainModel();
}
public void login(String username, String pwd) {
this.mainModel.login(username, pwd, new OnHttpResultListener() {
@Override
public void onResult(String result) {
if (getView() != null) {
getView().onLoginResult(result);
}
}
});
}
}
package com.tz.architect.mvp.simple4.framework.impl;
import com.tz.architect.mvp.simple4.framework.IMvpView;
/**
* View接口
* @author Dream
*
*/
public interface MainView extends IMvpView{
public void onLoginResult(String result);
}
package com.tz.architect.mvp.simple4.framework.impl;
import com.tz.architect.mvp.simple4.framework.AbsMvpPresenter;
import com.tz.architect.mvp.simple4.framework.IMvpView;
import android.app.Activity;
import android.os.Bundle;
public abstract class MvpActivity<V extends IMvpView, P extends AbsMvpPresenter<V>>
extends Activity {
private P presenter;
private V view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (presenter == null) {
this.presenter = bindPresenter();
}
if (view == null) {
this.view = bindView();
this.presenter.attachView(this.view);
}
}
/**
* 绑定P层
*
* @return
*/
public abstract P bindPresenter();
/**
* 绑定View层
*
* @return
*/
public abstract V bindView();
public P getPresenter() {
return presenter;
}
public V getView() {
return view;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (this.presenter != null) {
this.presenter.detachView();
}
}
}
即便如此,这个框架仍然不完整,因为我们这里只有Activity而缺少Fragment,我们继续完善;