序言
一年前,接觸了自己的第一個MVP的Android項目,當時仔細研究過一段時間這種架構,但是后來一直負責的項目都是基于MVC,現在都要忘記了,現在把他記錄下來。
背景
什么叫MVP,為什么要用MVP?
15年實習的時候,很幸運開始從0開始做第一個項目,老大把框架搭建起來,后來開始寫業務邏輯代碼,當時所在的公司是做電商的,需求那就是3個字,改,改,改,Activity的代碼上千行已經習以為常,這樣的代碼變得非常臃腫,難以維護。
那么什么叫MVP?就是Modle——View——Presenter,看到這個概念很懵逼,先甩一張圖看一下和普通MVC的區別:
從圖中可以很明顯的看到MVC和MVP的區別,MVP消除了View和Model之間的相互依賴,中間通過Presenter來通訊,解耦合。
MVC和MVP各個部分在干什么?
MVC和MVP兩個單詞只差了一個字母,但是兩種處理方式上改變得太多。
MVC三個部分分別在干什么
- View:對應于布局文件,但是細細的想一想這個View對應于布局文件,其實能做的事情特別少,實際上關于布局文件中的數據綁定的操作,事件處理的操作都在Activity中,造成了Activity既像View又像Controller。
- Model:業務邏輯和實體模型
- Controller:對應于Activity
MVP三個部分分別在干什么
- View:對應于Activity,負責View的繪制以及用戶交互
- Model:業務邏輯和實體邏輯
- Presenter:負責完成View與Model之間的交互
寫一個MVP的demo
- 定義Modle
1.定義bean類:
public class People {
public int icon;
public String like;
public String style;
public int getIcon() {
return icon;
}
public String getLike() {
return like;
}
public String getStyle() {
return style;
}
public void setIcon(int icon) {
this.icon = icon;
}
public void setLike(String like) {
this.like = like;
}
public void setStyle(String style) {
this.style = style;
}
@Override
public String toString() {
return "Pepole{" +
"icon=" + icon +
", like='" + like + '\'' +
", style='" + style + '\'' +
'}';
}
2.定義Model接口:
public interface IPeopleModel {
void loadPeople(PeolpleOnLoadListener girlOnLoadListener);
interface PeolpleOnLoadListener{
void onComplete(List<People> girls);
}
}
model處理數據邏輯:
public class PeopleModelImpl implements IPeopleModel {
Handler handler = new Handler(Looper.getMainLooper());
@Override
public void loadPeople(final PeolpleOnLoadListener peopleOnLoadListener) {
new Thread() {
@Override
public void run() {
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
final List<People> data = new ArrayList<People>();
data.add(new People(R.drawable.f1, "五顆星", "美女1"));
data.add(new People(R.drawable.f2, "四顆星", "美女2"));
data.add(new People(R.drawable.f3, "五顆星", "美女3"));
data.add(new People(R.drawable.f4, "三顆星", "美女4"));
data.add(new People(R.drawable.f5, "五顆星", "美女5"));
data.add(new People(R.drawable.f6, "三顆星", "美女6"));
data.add(new People(R.drawable.f7, "四顆星", "美女7"));
data.add(new People(R.drawable.f8, "五顆星", "美女8"));
data.add(new People(R.drawable.f9, "四顆星", "美女9"));
data.add(new People(R.drawable.f10, "三顆星", "美女10"));
handler.post(new Runnable() {
@Override
public void run() {
//回調
peopleOnLoadListener.onComplete(data);
}
});
}
}.start();
}
}
- 定義Presenter
從上面的圖中可以看到,Presenter依賴了Model,也依賴了View,所以應該持有他們的引用。因為項目中會有很多的Presenter需要去編寫,也有很多的代碼會重復,所以把這些共有的東西進行封裝,放到BasePresenter中。
1.編寫基類Presenter
public abstract class BasePresenter<T> {
protected WeakReference<T> mViewRef;
public abstract void fetch();
public void attachView(T view){
mViewRef = new WeakReference<T>(view);
}
public void detach(){
if(mViewRef!=null){
mViewRef.clear();
mViewRef = null;
}
}
}
2.編寫具體的Presenter
public class PeoplePresenterImpl extends BasePresenter<IPeopleView> {
IPeopleView iGirlView;
public PeoplePresenterImpl(IPeopleView iGirlView){
this.iGirlView = iGirlView;
}
IPeopleModel peopleModel = new PeopleModelImpl();
@Override
public void fetch() {
iGirlView.showloading();
if(peopleModel!=null){
peopleModel.loadPeople(new IPeopleModel.PeolpleOnLoadListener() {
@Override
public void onComplete(List<People> peoples) {
iPeopleView.showPeople(peoples);
}
});
}
}
}
- 定義View。
1.先確定我們的View中干什么,定義功能接口
public interface IPeopleView {
void showloading();
void showPeople(List<People> girls);
}
2.因為View要依賴Presenter,所以我們每個Activity肯定會持有一份Presenter的引用,所以在Activity銷毀的時候要釋放,這些操作放到BaseActivity中。
public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity {
protected T mPresent;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresent = createPresent();
mPresent.attachView(this);
}
@Override
protected void onDestroy() {
mPresent.detach();
super.onDestroy();
}
public abstract T createPresent() ;
}
3.編寫具體的Activity
public class MainActivity extends BaseActivity<PeoplePresenterImpl> implements IPeopleView {
private ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_v1);
listView = (ListView) findViewById(R.id.listview);
mPresent.fetch();
}
@Override
public PeoplePresenterImpl createPresent() {
return new PeoplePresenterImpl(this);
}
@Override
public void showloading() {
Toast.makeText(this,"正在拼命加載",Toast.LENGTH_SHORT).show();
}
@Override
public void showPeople(List<People> girls) {
listView.setAdapter(new GirlListAdapter(this,girls));
}
}
OK,MVP架構的練手項目就已經寫完了。可以看到MVP模式中,Activity的代碼會少很多。只是在展示,數據的處理和業務放在了model和presenter中。
效果圖:
歸納(套路)
現在,每個人應該都聽過一個詞,叫套路,沒錯,做任何事情都有套路,把套路搞清楚了,再復雜的東西變得簡單一些,總結一下編寫MVP模式的套路:
- View:負責繪制UI元素,與用戶進行交互,也就是我們的Activity。他可以從模型中讀取數據,但是不能修改或更新模型。view提供提供UI交互,在presenter的控制下修改UI,將業務事件交由presenter處理,注意: View層不存儲數據,不與Model層交互,在Android中View層一般是Activity、Fragment、View(控件)、ViewGroup(布局等)等
- Activity interface:需要View實現的接口,View通過View interface與Presenter進行交互,降低耦合,方便進行單元測試
- Model:負責存儲、檢索、操縱數據(有時也實現一個Model interface用來降低耦合);表示數據模型和業務邏輯(business logic)。模型并不總是DataSet,DataTable之類的東西,它代表著一類組件(components)或類(class),這些組件或類可以向外部提供數據,同時也能從外部獲取數據并將這些數據存儲在某個地方。簡單的理解,可以把模型想象成“外觀類(facade class)。職責就是從網絡,數據庫,文件,傳感器,第三方等數據源讀寫數據, 對外部的數據類型進行解析轉換為APP內部數據交由上層處理,對數據的臨時存儲,管理,協調上層數據請求。
- Presenter:作為View與Model交互的中間紐帶,處理與用戶交互的負責邏輯。
MVP的好處
- 減少了Activity的職責,簡化了Activity中的代碼,將復雜的邏輯代碼提取到了Presenter中進行處理。與之對應的好處就是,耦合度更低
- Activity代碼變得更加整潔,使用MVP之后,Activity就能瘦身許多了,基本上只有FindView、SetListener以及Init的代碼。其他的就是對Presenter的調用,還有對View接口的實現。這種情形下閱讀代碼就容易多了,而且你只要看Presenter的接口,就能明白這個模塊都有哪些業務,很快就能定位到具體代碼。Activity變得容易看懂,容易維護,以后要調整業務、刪減功能也就變得簡單許多。
- 方便進行單元測試,般單元測試都是用來測試某些新加的業務邏輯有沒有問題,如果采用傳統的代碼風格(習慣性上叫做MV模式,少了P),我們可能要先在Activity里寫一段測試代碼,測試完了再把測試代碼刪掉換成正式代碼,這時如果發現業務有問題又得換回測試代碼,咦,測試代碼已經刪掉了!好吧重新寫吧……
MVP中,由于業務邏輯都在Presenter里,我們完全可以寫一個PresenterTest的實現類繼承Presenter的接口,現在只要在Activity里把Presenter的創建換成PresenterTest,就能進行單元測試了,測試完再換回來即可。萬一發現還得進行測試,那就再換成PresenterTest吧。
MVP的壞處
接手的MVP的大項目中,有時候層層回調讓我不知所云,雖然代碼的耦合降低,但是讓人一看,會有很懵逼的感覺,代碼寫太多了,沒有MVC好上手。不知道大家有沒有這種感覺。哈哈!!
總結
沒有最好的架構,只有最合適的架構,雖然MVP架構很好,但是在小項目中,MVC就已經很好的支持項目的開發,沒必要用MVP。在大項目中可以考慮MVP的架構。搞定,收工。