上一篇我们大致了解了项目的结构组成,这一篇我们来分别说说里面的细节。
首先,搭建框架,就要根据框架的特点去完成初步的搭建。根据MVP的官网描述,我们首先完成MVP框架的基础构建,每一个activity都是一个V,一个V对应一个Presenter,所以在每一个界面的包下面,就至少包含了三个文件,IXXXConstane(V、P接口描述类)、XXXActivity(V)、XXXPresenter(P),每一个类的职责也必须清晰,XXXActivity主要用来作显示界面使用,不能包含任何逻辑,XXXPresenter主要用来沟通数据模块和V,数据改变了,就应该通知相应的界面控件进行改变,这是MVP框架中很重要的思想,每一个模块都只是完成自己的功能,不能逾越。
来看代码,在每一个V中都持有P的引用,那么在V中是可以调用P中的逻辑的,但我们一般不这么做,在V中持有P的引用,我们只是为了初始化P,让P和V关联起来,而不是直接去调用P中的逻辑,这点比较重要,不然结构就比较混乱了。比如在MainActivity中,就持有IMainConstane.IMainPresenter的引用,但我们一般不直接调用IMainPresenter中的逻辑:
public class MainActivity extends BaseActivity implements IMainConstane.IMainView {
@Inject
IMainConstane.IMainPresenter mMainPresenter;
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.text);
DaggerMainComponent.builder()
.userModelComponent(((MyApplication) getApplicationContext()).getUserModelComponent())
.mainModule(new MainModule(this))
.build()
.inject(this);
}
public void onViewClick(View view) {
switch (view.getId()) {
case R.id.login:
// 这里打印一下用户模块对象的地址,来判断@Singleton单利模式是否有用
mMainPresenter.logUserHashcode();
mMainPresenter.login("", "");
break;
case R.id.view_detail:
startActivity(new Intent(getApplicationContext(), SecondActivity.class));
break;
}
}
@Override
public void setTextMessage(String message) {
if (TextUtils.isEmpty(message)) {
tv.setText("");
} else {
tv.setText(message);
}
}
}
可以看到,我在V中,是没有直接调用P中的逻辑代码的,但有一种情况例外,就是事件交互,在人机进行交互的过程中,我们需要进行一些数据的操作,这个时候直接调用是没问题的。
我所说的只是“最好不要在V中直接调用P的代码,并不是一定不能,V中调用P的代码会显得结构层次有点乱,但如果业务需要,那也没办法”
看完V,我们再来说说P,P的构造函数中就传递进来两个参数,分别为M和V,这也就是为什么P是整个框架的逻辑处理核心的原因了,因为同时持有两者,那么就可以使用两者暴露出来的接口进行整个逻辑的操作和判断了。
public class MainPresenter implements IMainConstane.IMainPresenter {
private final IMainConstane.IMainView mView;
private final IUserModel mUserModel;
public MainPresenter(IMainConstane.IMainView view, IUserModel usermodel) {
this.mView = view;
this.mUserModel = usermodel;
}
@Override
public void login(String userName, String password) {
mUserModel.login(userName, password, new OnLoginListener() {
@Override
public void loginSuccess() {
mView.setTextMessage("login Success");
}
@Override
public void loginFaile(String message) {
mView.setTextMessage("login Faile");
}
});
}
@Override
public void logUserHashcode() {
mUserModel.logUser();
mUserModel.logOkHttp();
mUserModel.logRetrofit();
}
}
看上面的代码,当界面点击之后,会调用P中的login方法,也就是上面的login方法了,而该方法中则调用的是数据模块中的登录方法,我们跟进去看一下,就会发现调用到了UserModelImpl里面的登录方法,这样,整个逻辑就很清楚了,界面发起数据请求,通过P转到数据层,数据层通过回调接口(OnLoginListener)回调给P,然后P再根据持有的V进行界面的更改,也就是
mView.setTextMessage("login Success");
和
mView.setTextMessage("login Faile");
其实在这里是有一个歧义和区别的,你可能注意到了model包下的otherModel,它代表的是你项目中除用户数据模块之外的其它数据模块,比如我应用里面还需要产品数据模块,那么在P中如何将这些数据模块传递进去呢?
总不能修改P的构造函数,将所有数据模块依次传递进去吧?这样做先不说修改构造函数有多么繁琐,就算有dagger2我也不推荐这样使用。
我觉得,数据模块因该统一一个入口,然后里面应该分别持有所需模块,然后将统一入口提供给P。当然,这只是我想的,每个人对这个地方的处理可能不一样,有些人喜欢分别传入,也有些人喜欢统一入口方便管理,这就看个人喜好了!
最好还想看一下数据模块的组成的,但看了一下,觉得没什么好说的,代码写的很详细也简单易懂,主要就是接口编程,一个接口类IUserModel,一个实现类UserModelImpl。
这个地方不明白的可以私聊我!
详细的Dagger2+MVP融合,一行一行分析,一点一点进步,之一