MVP的一些小问题
说到Android应用开发的架构,大多数人可能都会说出MVP。
的确,MVP架构的出现为我们的Activity和Fragment的减肥瘦身做出的很大的贡献。但是基于一些原因,我和小伙伴们决定无情的将他抛弃,寻求更好的方案。
起因是这样的,在一次迭代中,我们决定将DI(dagger2)引入到项目中,而且我们顺理成章的认为,MVP也可以通过注入方式初始化,想象的代码能变成这样:
class MainActivity implement MainView{
@Inject MainPresenter presenter;
...
}
class MainPresenter {
@Inject MainView view;
@Inject MainModel model;
...
}
但在后来接入的过程中,我们发现这个方案没有办法直接走通。因为在MainPresenter
中注入view时,dagger并不知道view的实例从何而来,因此我们需要借助一个Module来把view的实例provide出来:
@Module
class MainModule {
MainActivity activity;
MainMoudle(MainActivity activity) { this.activity = activity; }
@Provide MainView mainView(){ return activity; }
}
这意味着,每个Activity都需要建立一个对应的Module,这样每次初始化Component的成本,和我们用传统方式在Presenter里绑定View的成本没有什么区别。
传统方式绑定:
class MainActivity implement MainView {
MainPresenter presenter;
void onCreate(){
presenter = new MainPresenter(this);
}
}
的确,对于DI的使用来说,这种绑定方式理所应当。究其原因,是因为在MVP中,View和Presenter互相引用了。
除此之外,相互引用还会造成很多问题。比如异步操作可能会造成View无法回收;比如Presenter大多时候无法重用。。
结合J2EE成熟的后端架构来看。MVP做到了分离界面和业务代码,但是模块化,分层,可复用等等的功能都比较难使用。所以与其说是一种“架构”,不如称之为一种“代码优化方式”更为恰当。
MVVM怎么做
假设需求是读取手机联系人并显示,MVP的做法是:
class Presenter {
void loadContacts(){
new Thread(()->{
// .... 加载联系人
view.showContacts(contactsList);
}).start();
}
}
interface View {
void showContacts(List contactsList);
}
MVVM也类似,但是他不直接把列表传给view,而是传给一个可订阅对象,在RxJava里我们使用Subject
:
class ViewModel {
Subject,List> loadContactsObs = PublishSubject.create();
void loadContacts() {
new Thread(()->{
// .... 加载联系人
loadContactsObs.onNext(contactsList); // 让subject接收联系人列表
}).start();
}
}
class Activity {
ViewModel viewModel;
void onCreate(){
viewMode.loadContactsObs.subscribe(list ->
this.showContacts(list));
viewMode.loadContacts();
}
}
这样一来,Actvity持有ViewModel,ViewModel进一步持有处理业务关系的模块。每个模块之间不会有互相的引用。这样带来的好处有:
首先,我们的注入代码变成了类似:
class MainActivity {
@Inject MainViewModel viewModel;
...
}
class MainViewModel {
@Inject MainModel model;
...
}
其次,ViewModel真正实现了可重用。不像实现Presenter必须实现对应的View,使用ViewModel我们可以灵活选择订阅/不订阅相应的返回事件。
还有,由于订阅的是Rx的Subject,我们可以使用Rx带的强大的api直接进行逻辑优化~
大概的介绍就是这样,实际开发过程中也出现了不少的问题和可优化的地方,例如MVP面向接口编程的思想,同样可以应用到MVVM中;例如MVVM结合DI的进一步优化等等,需要之后好好整理一下。