mvp指的是一种框架,Model,View,Presenter,三部分组成。
优点是解耦,缺点是项目中类的数量指数增长。并且由于接口的大量使用,导致接手的人读代码时没有其他模式那么轻松。
上图是mvp模式的架构图,可以看到Presenter连接了Model和View,所有的逻辑处理放在了此处。view 层想获取数据,就去调用Presenter的方法来获取,presenter调用model的方法去获取数据,model通过网络或者其他方式得到数据后,通过接口或者回调的方式把数据给Presenter,Presenter再把数据给view。
m-p,p-v,并不是需要互相持有的,可以通过接口回调的方式回传数据。
下面通过一个小例子,展示最简单的mvp是什么样子的。
流程主要是在view点击按钮,调用到presenter的方法,通过presenter调用model层面的方法获取到数据,然后将数据回调到presenter层面,presenter使用view层面的接口来把数据返回给view。
为了避免类太多,不好管理,现在引入一个contract来 管理。
分析一下, 点击按钮, 获取数据, 那Presenter就需要提供一个方法给View使用, getList,Model也需要给Presenter提高一个方法getList来使用。
public interface presenter{
void getList();
}
public interface model{
void getList(MainContract.CallBack callBack);
}
public interface CallBack{
void modelCallBack(String str);
}
这里给model的方法添加一个callback回调,方便回传数据。数据回到Presenter,怎么回到view呢,给view添加一个接口,包含两个方法,一个成功一个失败的。
public interface view {
void onSuccess(ArticleList data);
void onFail(ArticleList err);
}
参数是自定义的bean对象。
完整的贴一下
public class MainContract {
public interface view {
void onSuccess(ArticleList data);
void onFail(ArticleList err);
}
public interface model{
void getList(MainContract.CallBack callBack);
}
public interface CallBack{
void modelCallBack(String str);
}
public interface presenter{
void getList();
}
}
contract 的代码基本上就是这些 很简单,看一下view,用的Fragment
public class MainFragment extends Fragment implements MainContract.view{
MainContract.presenter presenter;
Button button;
TextView textView;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.main_fragment,container,false);
presenter = new MainPresenter(this);
button = view.findViewById(R.id.button);
textView = view.findViewById(R.id.textView);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
presenter.getList();
Log.e("--","bt 获取List");
}
});
return view;
}
@Override
public void onFail(ArticleList err) {
Log.e("---", err.getErrorMsg());
}
@Override
public void onSuccess(ArticleList data) {
Log.e("---", data.getData().getDatas().get(0).getTitle());
textView.setText(data.getData().getDatas().get(0).getTitle());
}
}
上面代码可以看到, MainFragment 实现了MainContract.view接口,然后new了一个MainPresenter,调用了presenter.getList()方法。现在来看一下MainPresenter类:
public class MainPresenter implements MainContract.presenter {
public MainContract.model model;
public MainContract.view view;
public MainPresenter(MainContract.view View) {
model = new MainModel();
this.view = View;
}
@Override
public void getList() {
model.getList(new MainContract.CallBack() {
@Override
public void modelCallBack(String str) {
ArticleList list = new Gson().fromJson(str,ArticleList.class);
if (list.getErrorCode()==0){
view.onSuccess(list);
}else {
view.onFail(list);
}
}
});
}
}
刚才初始化MainPresenter的时候传入的view,所以后面callback后可以调用view的success和fail方法把数据回传给view。
继续说, view层面调用了Presenter的getList方法, 可以看到MainPresenter初始化的时候创建了Mainmodel对象, 在getlist的时候调用了model的getList方法,然后new了一个callback方法,在这里根据返回数据返回给view。
下面看一下model 的 getList方法。
public class MainModel implements MainContract.model {
@Override
public void getList(final MainContract.CallBack callBack) {
new Thread(new Runnable() {
@Override
public void run() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url("https://www.wanandroid.com/article/list/0/json").build();
Call call = client.newCall(request);
try {
Response response = call.execute();
String str = response.body().string();
Log.e("---okhttp3",str);
callBack.modelCallBack(str);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
代码很简单, 开了线程去访问了wanAndroid的api。获取了数据后使用callback把数据返回。
现在梳理一下流程。
MainFragment里面的点击事件—>> MainPresenter.getList ---->>>MainModel.getList( callback 接口) —>>>MainModel获取数据,然后callback返回数据给MainPresenter,MainPresenter里面调用view接口的方法把数据回传到view。
经过一系列的传啊传,数据回到了view, 先稍等一下, 现在数据还不能直接setText,因为还是在子线程里面,可以setText试试,看是不是会报错。可以打印一下线程,就知道了。
最简单的MVP就这样,下一步开始封装简化一下,解决一下通用的问题, 比如说获取数据的时候,view 销毁了,然后获取到数据的时候回调, 就会出现空指针现象,还有一些通用的方法可以封装复用。