Android MVP模式實踐總結(jié)(附帶簡單例子)
最近在做畢業(yè)設(shè)計汰具,題目不是很難,但是后來發(fā)現(xiàn)功能有點多吟孙,感覺是苦力活聚蝶,還好用的是設(shè)計MVP模式,雖然剛開始也不是特別理解巷挥,不過寫的東西多就會發(fā)現(xiàn)這個模式的好處恰聘。
- 代碼雖然好像多了吸占,但思路非常清晰凿宾,敲代碼的速度特別快初厚,特別酸爽。
- 出現(xiàn)Bug很容易知道哪里出差錯排作。
粽子亚情,感覺開發(fā)效率快了,然后錯誤少了衫生。
感覺土浸,學MVP最好還是看下官方的介紹,如果英文過得去的話泪酱。
https://github.com/googlesamples/android-architecture/tree/todo-mvp/
首先, 為什么不用傳統(tǒng)的MVC(model, view, controller)呢还最,因為傳統(tǒng)的MVC模式在Android中憋活,把View和controller都雜糅到Activity當中,導致Activity越來越臃腫,使得難以開發(fā)和維護橱乱。而MVP(model, view, presenter)模式中,則把一些業(yè)務邏輯分離出來放到Presenter中作瞄,在MVP中的M(model)和V(view)是沒有箭頭連接的危纫,也就是MVP中(View只需要做些與用戶相關(guān)的操作,即繪制界面以及與用戶交互)契耿,其他的業(yè)務功能邏輯都交由Presenter來完成。

在官方介紹中有Contract這個Interface,里面定義了View和Presenter接口透敌,其實Contract在mvp中什么角色也不是酗电,只是把view和presenter定義在一起方便管理内列。
現(xiàn)在給個簡單例子:
- 假設(shè)我們要從網(wǎng)絡獲取數(shù)據(jù)并顯示用戶的個人界面话瞧;
- 同時我們可以更改用戶數(shù)據(jù)并上傳到服務器;
- 為了簡單起見并不真正的獲取網(wǎng)絡數(shù)據(jù)移稳,也省去了異步等操作,在實際開發(fā)中都是要考慮的个粱。
首先來看View和Presenter的接口定義
public interface BasePresenter {
void start();
}
public interface BaseView<T> {
void setPresenter(T presenter);
}
public interface UserInfoContract {
interface Presenter extends BasePresenter{
void loadUserInfo();
void changeAge();
}
interface View extends BaseView<Presenter>{
void showUserInfoView(UserInfo userInfo);
void showErrorMessage(String errMsg);
UserInfo getUserInfo();
}
}
可以看到在BaseView這個基類接口中含有setPresenter古毛,從圖上也可以看出,View和Presenter是可以相互交換數(shù)據(jù)的都许,所以一般都會互相持有對方的引用稻薇。我們再看看它們的實現(xiàn)類。
public class UserInfoActivity extends AppCompatActivity implements UserInfoContract.View{
TextView nameTxt;
TextView ageTxt;
EditText newAgeEdit;
Button change;
UserInfoContract.Presenter presenter;
UserInfo info;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_info);
nameTxt = (TextView)findViewById(R.id.name);
ageTxt = (TextView)findViewById(R.id.age);
newAgeEdit = (EditText)findViewById(R.id.new_age);
change = (Button)findViewById(R.id.change);
change.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String str = newAgeEdit.getText().toString();
if(str.length() !=0 ){
int ageInt = Integer.valueOf(str);
info.setAge(ageInt);
presenter.changeAge();
}
}
});
//創(chuàng)建Presenter
UserInfoContract.Presenter presenter =new UserInfoPresenter(this);
}
@Override
public void showUserInfoView(UserInfo userInfo) {
//更新用戶界面
info = userInfo;
nameTxt.setText(userInfo.getName());
ageTxt.setText(userInfo.getAge() + "");
}
@Override
public void showErrorMessage(String errMsg) {
//顯示錯誤信息
Toast.makeText(this,errMsg,Toast.LENGTH_LONG).show();
}
@Override
public UserInfo getUserInfo() {
return info;
}
@Override
public void setPresenter(UserInfoContract.Presenter presenter) {
this.presenter = presenter;
}
}
public class UserInfoPresenter implements UserInfoContract.Presenter {
UserInfoContract.View view;
UserInfo info;
public UserInfo getNetworkData(){
//模擬網(wǎng)絡io等操作胶征,獲取數(shù)據(jù)
if(info == null){
info = new UserInfo();
info.setAge(16);
info.setId(1);
info.setName("lawliex");
}
return info;
}
public UserInfoPresenter(UserInfoContract.View view) {
this.view = view;
view.setPresenter(this);
start();
}
@Override
public void loadUserInfo() {
UserInfo userInfo = getNetworkData();
//取得數(shù)據(jù)后塞椎,因為presenter持有view的引用,可以回調(diào)view的相關(guān)方法睛低,此處假設(shè)加載數(shù)據(jù)成功案狠,回調(diào)顯示正常界面
view.showUserInfoView(userInfo);
//如果發(fā)生錯誤則回調(diào)
//view.showErrorMessage("error");
}
@Override
public void changeAge() {
info = view.getUserInfo();
//模擬更新數(shù)據(jù)到服務器钱雷,成功后骂铁,更新View的數(shù)據(jù)
start();
}
@Override
public void start() {
loadUserInfo();
}
}
項目源碼