Android MVP模式浅析
1.什么是MVP?为什么使用MVP?
View:负责界面刷新、布局等一切和UI界面相关的操作;
Presenter:连接Model和View的桥梁,属于逻辑实现层;
Model :负责数据相关的工作,比如去网络获取数据,去数据库读取数据等;
MVP模式中,Presenter(以下简称:P)层同时持有Model(以下简称:M)和View(以下简称:V)的对象引用,可以对M和V进行操作,这也是P层的职责所在,而V与M之间是没有任何关联的,完全解耦,M通过回调的方式返回值给P,而V只持有P的对象引用。
优点:高度解耦,方便项目维护,方便功能测试,维护起来更加的便利,简化Activity文件结构等,
缺点:需要编写大量的接口。
接下来我将写一个超级简单的登录Demo来演示MVP的使用,首先我们先来看看项目的基本结构
接下来我们来看看如何分别实现各层之间的逻辑,先来看看Model层的实现。
2.Model层实现
由于model并未持有presenter层的引用,所以我们一般使用回调的方式将model层执行后的结果返回给presenter层,所以我们在这首先定义了一个ILoginCallback的回调函数,方便LoginModel将登录结果返回给LoginPresenter,我们来看看这个回调函数的定义:
public interface ILoginCallback<T> {
//登录成功的回调方法
void success(T t);
//登录失败的回调方法
void failed(String value);
}
接下来我们来看看ILoginModel接口的定义,首先既然我们的接口里面的方法是定义给LoginPresenter使用的,就需要先明白在登录时P层需要做什么操作,很明显登录的话最少需要一个进行登录请求的方法,所以ILoginModel接口的定义如下:
public interface ILoginModel {
//进行网络操作:登录请求
void loginRuquest(ILoginCallback<UserInfo> iLoginCallback, UserInfo userInfo);
}
既然接口已经定义了,接下来看看LoginModel的具体实现,逻辑很简单,就是做一个简单模拟登录的操作,匹配成功调用LoginCallback的success()方法,否则调用failed()方法。
public class LoginModel implements ILoginModel {
@Override
public void loginRuquest(final ILoginCallback<UserInfo> iLoginCallback, final UserInfo userInfo) {
//模拟去服务器验证用户名和密码的操作
new Thread() {
@Override
public void run() {
super.run();
try {
Thread.sleep(3000);
if (userInfo.getUserName().equals("123") && userInfo.getPassword().equals("123")) {
iLoginCallback.success(userInfo);
}else{
iLoginCallback.failed("登录失败");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
}
接下来我们来看看如何实现View层的逻辑。
3.View层实现
首先我们先来看看接口的实现,上面说过接口中的方法是用来给P层调用的,所以我们需要思考一下登录的过程中Activity需要哪些操作,在这里我就简单的定义了一下行为,如下:
public interface ILoginView {
//提示登录信息
void showMessage(String values);
//清楚登录信息
void clearText();
//登录信息
void loginInfo();
//登陆成功的界面
void startMain();
}
OK,接下来来看看LoginView对ILoginView接口的实现:
public class LoginActivity extends BaseActivity implements ILoginView {
private Button btnLogin, btnClear;
private EditText etName, etPswd;
private ProgressBar pBar;
private final static String TAG = "felix";
private MyHandler myHandler = new MyHandler(this);
public static class MyHandler extends Handler {
WeakReference<Context> reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
LoginActivity activity = (LoginActivity) reference.get();
if (msg.what == 1) {
activity.pBar.setVisibility(View.GONE);
Toast.makeText(activity, msg.obj.toString() ,Toast.LENGTH_SHORT).show();
}
}
}
//将Activity引用传递给presenter
@Override
protected ILoginPresenter getLoginPresenter() {
return new LoginPresenter(this);
}
//加载布局
@Override
protected int initLayoutView() {
return R.layout.activity_ilogin;
}
//初始化View
@Override
protected void initView() {
etName = findViewById(R.id.et_name);
etPswd = findViewById(R.id.et_pswd);
btnLogin = findViewById(R.id.btn_login);
btnClear = findViewById(R.id.btn_clear);
pBar = findViewById(R.id.progress_bar);
pBar.setVisibility(View.GONE);
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
loginInfo();
pBar.setVisibility(View.VISIBLE);
}
});
btnClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
loginPresenter.clear();
}
});
}
//提示登录结果
@Override
public void showMessage(String values) {
Message message = Message.obtain();
message.what = 1;
message.obj = values;
myHandler.sendMessage(message);
}
//清除EditText
@Override
public void clearText() {
etName.setText("");
etPswd.setText("");
}
//获取用户信息
@Override
public void loginInfo() {
loginPresenter.loadData(etName.getText().toString(), etPswd.getText().toString());
}
@Override
public void startMain() {
//MainActivity只是一个空的activity
startActivity(new Intent(this, MainActivity.class));
}
@Override
protected void onDestroy() {
super.onDestroy();
myHandler.removeCallbacksAndMessages(null);
}
}
//获取用户信息
@Override
public void loginInfo() {
loginPresenter.loadData(etName.getText().toString(), etPswd.getText().toString());
}
@Override
public void startMain() {
//MainActivity只是一个空的activity
startActivity(new Intent(this, MainActivity.class));
}
@Override
protected void onDestroy() {
super.onDestroy();
myHandler.removeCallbacksAndMessages(null);
}
}
上面的逻辑很简单,关键地方都加了注释,就不解释了,在这贴一下BaseActivity的代码:
public abstract class BaseActivity extends Activity {
public ILoginPresenter loginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//加载布局
setContentView(initLayoutView());
//初始化ILoginPresenter的引用
initView();
loginPresenter = getLoginPresenter();
}
//获取ILoginPresenter的引用
protected abstract ILoginPresenter getLoginPresenter();
//让子类实现并返回布局文件ID
protected abstract int initLayoutView();
//绑定控件
protected abstract void initView();
}
接下来,我们来看看如何实现这个桥梁吧。
4.Presenter层实现
上面我们已经说过,P层需要同时持有Model和View的引用,所以,我们需要在构造方法中将Model层的引用new出来,而View层的引用因为需要调用调用P里面的方法(即P->V,V->P),所以我们在View层获取P层引用时将View自身传到了P层,以便于建立P与V之间的双向引用,我们先来看看P的构造方法:
public class LoginPresenter implements ILoginPresenter {
private ILoginView iLoginView;
private ILoginModel iLoginModel;
public LoginPresenter(ILoginView iLoginView) {
//获取V层传递过来的实例
this.iLoginView = iLoginView;
//new一个M层的实例
iLoginModel = new LoginModel();
}
}
现在,需要考虑P层的接口如何定义,上面说过,P层接口中的方法是暴露给View层来调用的,而我们又是登录的逻辑,所以在接口中肯定需要定义一个登录的方法来给View层调用,同时上面也定义了一下clear方法,因此,接口定义如下:
public interface ILoginPresenter {
void clear();
void loadData(String name, String pswd);
}
接下来我们来看看完整的LoginPresenter的实现:
public class LoginPresenter implements ILoginPresenter {
private ILoginView iLoginView;
private ILoginModel iLoginModel;
public LoginPresenter(ILoginView iLoginView) {
this.iLoginView = iLoginView;
iLoginModel = new LoginModel();
}
@Override
public void clear() {
iLoginView.clearText();
}
@Override
public void loadData(String name, String pswd) {
UserInfo userInfo = new UserInfo(name, pswd);
//调用M层的loginRequest()方法进行登录操作
iLoginModel.loginRuquest(new ILoginCallback<UserInfo>() {
@Override
public void success(UserInfo userInfo) {
//调用V层的showMessage()方法提示用户
iLoginView.showMessage(userInfo.getUserName()+"登录成功");
//调用V层的startMain()方法进行跳转
iLoginView.startMain();
}
@Override
public void failed(String value) {
iLoginView.showMessage("登录失败");
}
},userInfo);
}
}
至此,整个MVP模式就简单的分析完了~
5.总结
现在网上的MVP流程图可能很多都和我上面的不太一样,大多数都是如下所示:
其实也就是M层也持有P层的引用,区别就是需要在Model的构造方法中传入Presenter的引用,来让Presenter能调用到model的方法,但是本人不太喜欢这样使用,更喜欢通过回调的方式,感觉这样类与类之间的耦合更低!
呃呃,第一次写博客,如有理解不对的地方,请指正~