MVP的一些小問題
說(shuō)到Android應(yīng)用開發(fā)的架構(gòu),大多數(shù)人可能都會(huì)說(shuō)出MVP。
的確,MVP架構(gòu)的出現(xiàn)為我們的Activity和Fragment的減肥瘦身做出的很大的貢獻(xiàn)。但是基于一些原因,我和小伙伴們決定無(wú)情的將他拋棄,尋求更好的方案。
起因是這樣的,在一次迭代中,我們決定將DI(dagger2)引入到項(xiàng)目中,而且我們順理成章的認(rèn)為,MVP也可以通過(guò)注入方式初始化,想象的代碼能變成這樣:
class MainActivity implement MainView{
@Inject MainPresenter presenter;
...
}
class MainPresenter {
@Inject MainView view;
@Inject MainModel model;
...
}
但在后來(lái)接入的過(guò)程中,我們發(fā)現(xiàn)這個(gè)方案沒有辦法直接走通。因?yàn)樵?code>MainPresenter中注入view時(shí),dagger并不知道view的實(shí)例從何而來(lái),因此我們需要借助一個(gè)Module來(lái)把view的實(shí)例provide出來(lái):
@Module
class MainModule {
MainActivity activity;
MainMoudle(MainActivity activity) { this.activity = activity; }
@Provide MainView mainView(){ return activity; }
}
這意味著,每個(gè)Activity都需要建立一個(gè)對(duì)應(yīng)的Module,這樣每次初始化Component的成本,和我們用傳統(tǒng)方式在Presenter里綁定View的成本沒有什么區(qū)別。
傳統(tǒng)方式綁定:
class MainActivity implement MainView {
MainPresenter presenter;
void onCreate(){
presenter = new MainPresenter(this);
}
}
的確,對(duì)于DI的使用來(lái)說(shuō),這種綁定方式理所應(yīng)當(dāng)。究其原因,是因?yàn)樵贛VP中,View和Presenter互相引用了。
除此之外,相互引用還會(huì)造成很多問題。比如異步操作可能會(huì)造成View無(wú)法回收;比如Presenter大多時(shí)候無(wú)法重用。。
結(jié)合J2EE成熟的后端架構(gòu)來(lái)看。MVP做到了分離界面和業(yè)務(wù)代碼,但是模塊化,分層,可復(fù)用等等的功能都比較難使用。所以與其說(shuō)是一種“架構(gòu)”,不如稱之為一種“代碼優(yōu)化方式”更為恰當(dāng)。
MVVM怎么做
假設(shè)需求是讀取手機(jī)聯(lián)系人并顯示,MVP的做法是:
class Presenter {
void loadContacts(){
new Thread(()->{
// .... 加載聯(lián)系人
view.showContacts(contactsList);
}).start();
}
}
interface View {
void showContacts(List<Contacts> contactsList);
}
MVVM也類似,但是他不直接把列表傳給view,而是傳給一個(gè)可訂閱對(duì)象,在RxJava里我們使用Subject
:
class ViewModel {
Subject<List<Contacts>,List<Contacts>> loadContactsObs = PublishSubject.create();
void loadContacts() {
new Thread(()->{
// .... 加載聯(lián)系人
loadContactsObs.onNext(contactsList); // 讓subject接收聯(lián)系人列表
}).start();
}
}
class Activity {
ViewModel viewModel;
void onCreate(){
viewMode.loadContactsObs.subscribe(list ->
this.showContacts(list));
viewMode.loadContacts();
}
}
這樣一來(lái),Actvity持有ViewModel,ViewModel進(jìn)一步持有處理業(yè)務(wù)關(guān)系的模塊。每個(gè)模塊之間不會(huì)有互相的引用。這樣帶來(lái)的好處有:
首先,我們的注入代碼變成了類似:
class MainActivity {
@Inject MainViewModel viewModel;
...
}
class MainViewModel {
@Inject MainModel model;
...
}
其次,ViewModel真正實(shí)現(xiàn)了可重用。不像實(shí)現(xiàn)Presenter必須實(shí)現(xiàn)對(duì)應(yīng)的View,使用ViewModel我們可以靈活選擇訂閱/不訂閱相應(yīng)的返回事件。
還有,由于訂閱的是Rx的Subject,我們可以使用Rx帶的強(qiáng)大的api直接進(jìn)行邏輯優(yōu)化~
大概的介紹就是這樣,實(shí)際開發(fā)過(guò)程中也出現(xiàn)了不少的問題和可優(yōu)化的地方,例如MVP面向接口編程的思想,同樣可以應(yīng)用到MVVM中;例如MVVM結(jié)合DI的進(jìn)一步優(yōu)化等等,需要之后好好整理一下。