这篇文章介绍Android开发中MVP的概念以及实战MVP实例
MVP模式简介
- View 对应于Activity,负责View的绘制以及与用户交互;
- Model 依然是业务逻辑和实体模型;
- Presenter 负责完成View于Model间的交互
图例,其实很简单,Presenter起到了连接View和Model的任务,这样我们的Activity就不会再处理业务相关的繁琐代码。
那么MVP的关键也就是如何让Presenter层来完成View和Model之间的交互。
接下来我们就从具体的实际案例中来深入学习MVP模式的设计和编码。
案例很简单,我们就实现了一个列表:
- 加载数据前显示模拟loading的Toast;
- 模拟方法子线程加载数据;
- 加载数据完成隐藏loading,将数据显示在列表中。
项目目录结构
结构很清晰,我对View层和Presenter层都做了一个基类的抽象,来降低我们代码的耦合度。
View层
BaseView
public interface BaseView {
void showLoading();
void hideLoading();
}
这里只抽象了两个常用的试图操作的方法,你可以在这里增加更多的View操作的方法,比如showToast之类。
GirlView
public interface GirlView extends BaseView{
void setListItem(List<GirlEntity> data);
void showMessage(String message);
}
继承了BaseView,不仅持有了BaseView里面的view操作方法,同时新增了自己列表页面的View方法,显示列表数据,以及点击Item显示Message信息。
Presenter
BasePresenter
public class BasePresenter<T> {
public T mView;
public void attachView(T view){
this.mView = view;
}
public void dettach(){
mView = null;
}
}
这里在Presenter的基类里面我们使用了一个泛型T,因为我们不知道具体的Presenter层里面持有的到底是哪一个View,因此我们使用泛型来解决BasePresenter对View的持有。同时我们在BasePresenter中提供了两个方法,attacheView(View v)用来绑定Presenter和View,dettach()用来解绑Presenter和View层关系,这样我们可以实现有效的资源释放,避免出现OOM。
GirlPresenter
public class GirlPresenter extends BasePresenter<GirlView>{
private Handler mHandler;
public GirlPresenter() {
mHandler = new Handler(Looper.getMainLooper());
}
public void getGirls(){
mView.showLoading();
final List<GirlEntity> list = new ArrayList<>();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3*1000);
for (int i=0; i < 10; i++){
GirlEntity girlEntity = new GirlEntity();
girlEntity.name = "赵丽颖"+i;
girlEntity.imgRes = R.drawable.img;
list.add(girlEntity);
}
mHandler.post(new Runnable() {
@Override
public void run() {
mView.hideLoading();
mView.setListItem(list);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
这里我继承了BasePresenter,同时指定GirlPresenter持有了GirlView。然后我在GirlPresenter中实现了获取,绑定数据到View的操作,也就是实现了Model层和View层的交互。其实代码并不复杂,我才开始数据加载前用View层显示了loading,然后我在子线程处理了业务数据,处理完数据后我又切换至主线程完成了View层隐藏loading和数据显示。
Activity
Activity层主要是用来View层方法的实现以及Presenter层方法的初始化。
这里我同样抽象一个BaseActivity出来。
BaseActivity
public abstract class BaseActivity<V,T extends BasePresenter<V>> extends AppCompatActivity{
public T presenter;
/**
* 根据不同页面实现自己的P层
* @return
*/
public abstract T createPresenter();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
presenter = createPresenter();
//取的关联
presenter.attachView((V)this);
}
@Override
protected void onDestroy() {
super.onDestroy();
presenter.dettach();
}
}
同样,我在BaseActivity中用泛型定义了一个Presenter,因为我们不确定具体的某一个Activity中究竟需要哪个Presenter。因为Presenter持有View的泛型对象,因此这里需要使用双层泛型的方法实现。然后我通过一个静态方法createPresenter()巧妙的实现了具体Activity中具体Presenter的初始化。之后我将Presenter跟View的处理关系同Activity的生命周期关联,这样就完美的实现了绑定和解绑操作。
GirActivity
接下来就是我们具体的某一个Activity里的操作了。
public class GirlActivity extends BaseActivity<GirlView,GirlPresenter> implements GirlView{
private ListView mListView;
@Override
public GirlPresenter createPresenter() {
return new GirlPresenter();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.listview);
presenter.getGirls();
}
@Override
public void showLoading() {
Toast.makeText(this,"加载数据中...",Toast.LENGTH_SHORT).show();
}
@Override
public void hideLoading() {
Toast.makeText(this,"数据加载完成...",Toast.LENGTH_SHORT).show();
}
@Override
public void setListItem(List<GirlEntity> data) {
GirlAdapter adapter = new GirlAdapter(data,this);
mListView.setAdapter(adapter);
}
@Override
public void showMessage(String message) {
}
}
我们可以看到,Activity中的代码很简单也很清晰,我们初始化了GirlPresenter,这样GirlPresenter和GirlView就产生了关联,通过调用GirlPresenter中getGirls()方法,我们就可以获取列表数据,同时将View的操作回调到我们Activity实现的GirlView的方法中。
至此,MVP的使用就结束了。可能代码中涉及到一些基类的抽象,使得我们对MVP模式的理解略显复杂。但抽象是为了使我们简化代码,降低耦合度,相信这个工作是必须要做的。因此,建议大家自己动手,可以先不做抽象,单纯对某一个Activity进行某一个特定Presenter的实例化,让特定的Presenter持有特定的View,这样更方便大家的理解。当然理解完还是建议大家根据自己的理解和需要,对View和Presenter进行抽象,再对Activity进行抽象,这样就可以将MVP模式完美的应用到我们的项目中了。
如果对你有帮助,欢迎大家留言讨论,点赞关注!