15年年底本人公司新開的一個項目,用上了mvp模式開發(fā),那個時候還沒發(fā)現(xiàn)google出了mvp的demo。
首先什么是MVP:
- M-model,即javaBean 數(shù)據(jù)模型層;
- V-view,視圖層,常用的即Activity Fragment,這里是定義一個接口IView,Activity去實現(xiàn)IView的寫法;
- P-presenter,數(shù)據(jù)處理層,所有的數(shù)據(jù)邏輯,業(yè)務邏輯都在這里處理;
原來我的寫法
而當時我在寫mvp時只是簡單的寫成了:以下幾個類:
- UserInfoModel-model;
- IUserInfoView-IView;
- UserInfoActivity-Activity;
- UserInfoPresenter-Presenter;
接下來假設業(yè)務是這樣的:網(wǎng)絡請求用戶信息接口,并將用戶信息展現(xiàn)在UserInfoActivity中。
-
先看目錄結構:
目錄.png 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--實現(xiàn)的IUserInfoView:
public interface IUserInfoView {
String loadUserId();//假設接口請求需要一個userId
void showLoading();//展示加載框
void dismissLoading();//取消加載框展示
void showUserInfo(UserInfoModel userInfoModel);//將網(wǎng)絡請求得到的用戶信息回調
}
網(wǎng)絡接口請求用戶信息之前 獲得userId,然后展示loading,數(shù)據(jù)加載成功取消loading,最后將數(shù)據(jù)展示在Activity上
- 3:Presetner--UserInfoPresenter:這里就實現(xiàn)一個模擬的接口請求
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實現(xiàn)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中的方法的關聯(lián)。
Google demo寫法有所不同:google官方mvp寫法demo
下面用google官方demo的寫法實現(xiàn)上面的模擬業(yè)務:
-
首先看下目錄結構:
這里多了一個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);//將網(wǎng)絡請求得到的用戶信息回調
String loadUserId();//假設接口請求需要一個userId
}
interface Presenter extends BasePresenter {
void loadUserInfo();
}
}
契約內部有2個接口,分別繼承了BaseView和BasePresenter,View和Presenter中實現(xiàn)的方法分別是UI操作,和數(shù)據(jù)業(yè)務邏輯操作,此時是不是看的異常的清晰。
多了一個契約類,契約內部包含了2個接口,一個是Presenter一個是View,就相當于之前的寫法中的接口IView和普通類Presenter,只不過現(xiàn)在都將這兩個類所需要的業(yè)務和UI層的接口直接放在一起展現(xiàn)出來,變得很清晰。在契約接口中的Presenter是一個接口,需要我們去實現(xiàn),代碼如下:
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 構造函數(shù)中傳入UserInfoContract.View,并且調用view的setPresenter()方法;
2:將所有的初始化操作都放在start()方法中(這里demo只有一個:網(wǎng)絡請求獲取用戶信息),這樣只要進入界面的時候調用start()方法就可以執(zhí)行一系列初始化的操作,這就相當于一種約定好的開發(fā)。
- 最后看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實現(xiàn)了UserInfoContract.View中的setPresenter()方法;而UserInfoPresenter 構造函數(shù)中已經(jīng)調用了UserInfoContract.View中的setPresenter()方法;
兩者思想一樣,只是寫法不同。
后者demo地址