15年年底本人公司新开的一个项目,用上了mvp模式开发,那个时候还没发现google出了mvp的demo。
首先什么是MVP:
- M-model,即javaBean 数据模型层;
- V-view,视图层,常用的即Activity Fragment,这里是定义一个接口IView,Activity去实现IView的写法;
- P-presenter,数据处理层,所有的数据逻辑,业务逻辑都在这里处理;
原来我的写法
而当时我在写mvp时只是简单的写成了:以下几个类:
- UserInfoModel-model;
- IUserInfoView-IView;
- UserInfoActivity-Activity;
- UserInfoPresenter-Presenter;
接下来假设业务是这样的:网络请求用户信息接口,并将用户信息展现在UserInfoActivity中。
-
先看目录结构:
1:先看Model--UserInfoModel
public class UserInfoModel {
private String name;
private int age;
private String address;
public UserInfoModel(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
- 2:View--实现的IUserInfoView:
public interface IUserInfoView {
String loadUserId();//假设接口请求需要一个userId
void showLoading();//展示加载框
void dismissLoading();//取消加载框展示
void showUserInfo(UserInfoModel userInfoModel);//将网络请求得到的用户信息回调
}
网络接口请求用户信息之前 获得userId,然后展示loading,数据加载成功取消loading,最后将数据展示在Activity上
- 3:Presetner--UserInfoPresenter:这里就实现一个模拟的接口请求
public class UserInfoPresenter {
private IUserInfoView iUserInfoView;
public UserInfoPresenter(IUserInfoView iUserInfoView) {
this.iUserInfoView = iUserInfoView;
}
public void loadUserInfo() {
String userId = iUserInfoView.loadUserId();
iUserInfoView.showLoading();//接口请求前显示loading
//这里模拟接口请求回调-
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//模拟接口返回的json,并转换为javaBean
UserInfoModel userInfoModel = new UserInfoModel("小宝", 1, "杭州");
iUserInfoView.showUserInfo(userInfoModel);
iUserInfoView.dismissloading();
}
}, 3000);
}
}
- 4:View--UserInfoActivity实现IUserInfoView接口:
public class UserInfoActivity extends AppCompatActivity implements IUserInfoView {
private TextView tv_name;
private TextView tv_age;
private TextView tv_address;
private UserInfoPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_name = (TextView) findViewById(R.id.tv_name);
tv_age = (TextView) findViewById(R.id.tv_age);
tv_address = (TextView) findViewById(R.id.tv_address);
presenter = new UserInfoPresenter(this);
presenter.loadUserInfo();
}
@Override
public void showLoading() {
Toast.makeText(this, "正在加载", Toast.LENGTH_LONG).show();
}
@Override
public void dismissLoading() {
Toast.makeText(this, "加载完成", Toast.LENGTH_LONG).show();
}
@Override
public void showUserInfo(UserInfoModel userInfoModel) {
if (userInfoModel != null) {
tv_name.setText(userInfoModel.getName());
tv_age.setText(String.valueOf(userInfoModel.getAge()));
tv_address.setText(userInfoModel.getAddress());
}
}
@Override
public String loadUserId() {
return "1000";//假设需要查询的用户信息的userId是1000
}
}
这样写并没有错,只是不能更直观的看到IView中的方法和Presenter中的方法的关联。
Google demo写法有所不同:google官方mvp写法demo
下面用google官方demo的写法实现上面的模拟业务:
-
首先看下目录结构:
这里多了一个contract包:里面放的是契约接口。更能直接明了的看到View和Presenter之间的方法。
还多了一个BaseView,BasePresenter:看代码
public interface BasePresenter {
void start();
}
这里的start()方法就相当于约定了所有的Presenter的初始化操作都放在start()方法中;
public interface BaseView<T> {
void setPresenter(T presenter);
}
- 再来看契约类:UserInfoContract
public interface UserInfoContract {
interface View extends BaseView<Presenter>{
void showLoading();//展示加载框
void dismissLoading();//取消加载框展示
void showUserInfo(UserInfoModel userInfoModel);//将网络请求得到的用户信息回调
String loadUserId();//假设接口请求需要一个userId
}
interface Presenter extends BasePresenter {
void loadUserInfo();
}
}
契约内部有2个接口,分别继承了BaseView和BasePresenter,View和Presenter中实现的方法分别是UI操作,和数据业务逻辑操作,此时是不是看的异常的清晰。
多了一个契约类,契约内部包含了2个接口,一个是Presenter一个是View,就相当于之前的写法中的接口IView和普通类Presenter,只不过现在都将这两个类所需要的业务和UI层的接口直接放在一起展现出来,变得很清晰。在契约接口中的Presenter是一个接口,需要我们去实现,代码如下:
public class UserInfoPresenter implements UserInfoContract.Presenter {
private UserInfoContract.View view;
public UserInfoPresenter(UserInfoContract.View view) {
this.view = view;
view.setPresenter(this);
}
@Override
public void loadUserInfo() {
String userId = view.loadUserId();
view.showLoading();//接口请求前显示loading
//这里模拟接口请求回调-
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//模拟接口返回的json,并转换为javaBean
UserInfoModel userInfoModel = new UserInfoModel("小宝", 1, "杭州");
view.showUserInfo(userInfoModel);
view.dismissLoading();
}
}, 3000);
}
@Override
public void start() {
loadUserInfo();
}
}
1:UserInfoPresenter 构造函数中传入UserInfoContract.View,并且调用view的setPresenter()方法;
2:将所有的初始化操作都放在start()方法中(这里demo只有一个:网络请求获取用户信息),这样只要进入界面的时候调用start()方法就可以执行一系列初始化的操作,这就相当于一种约定好的开发。
- 最后看UserInfoActivity如何进行调用
public class UserInfoActivity extends AppCompatActivity implements UserInfoContract.View {
private TextView tv_name;
private TextView tv_age;
private TextView tv_address;
private UserInfoContract.Presenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_name = (TextView) findViewById(R.id.tv_name);
tv_age = (TextView) findViewById(R.id.tv_age);
tv_address = (TextView) findViewById(R.id.tv_address);
new UserInfoPresenter(this);
presenter.start();
}
@Override
public void showLoading() {
Toast.makeText(this, "正在加载", Toast.LENGTH_LONG).show();
}
@Override
public void dismissLoading() {
Toast.makeText(this, "加载完成", Toast.LENGTH_LONG).show();
}
@Override
public void showUserInfo(UserInfoModel userInfoModel) {
if (userInfoModel != null) {
tv_name.setText(userInfoModel.getName());
tv_age.setText(String.valueOf(userInfoModel.getAge()));
tv_address.setText(userInfoModel.getAddress());
}
}
@Override
public String loadUserId() {
return "1000";//假设需要查询的用户信息的userId是1000
}
@Override
public void setPresenter(UserInfoContract.Presenter presenter) {
this.presenter = presenter;
}
}
在onCreate()方法:
new UserInfoPresenter(this);
presenter.start();
而并没有写成
presenter=new UserInfoPresenter(this);
因为UserInfoActivity实现了UserInfoContract.View中的setPresenter()方法;而UserInfoPresenter 构造函数中已经调用了UserInfoContract.View中的setPresenter()方法;
两者思想一样,只是写法不同。
后者demo地址