1.概述
代码是两层开发,业务逻辑被内嵌与用户界面中,我们为了更好的维护,有必要将行为分离出来。我认为MVP模式就是基于这样一个理念。每个人都有一个自己的MVP模式。
2.我所理解的MVP模式
3.构建我的MVP模式
简要概述:展现类持有领域类的引用,职责就是与用户进行直接的信息交互,比如我们在Activity中持有Presenter引用,Activity的事件逻辑处理交给Presenter。领域类定义数据及其具体的逻辑处理,展现类的操作对象都指向领域类,领域类在需要展现类帮助的时候通过接口通知到展现类。
例如:
Activity中进行初始化的代码片段:
data = new ArrayList<>();
adapter = new MainAdapter(data);
binding.mainRecyclerView.setHasFixedSize(true);
binding.mainRecyclerView.setAdapter(adapter);
binding.mainRecyclerView.setLayoutManager(new GridLayoutManager(self, 4));
// binding.mainRecyclerView.addItemDecoration(new DividerGridItemDecoration(self));
itemTouchHelper = new ItemTouchHelper(new SwipItemTouchCallback(adapter).setOnDragListener(this));
itemTouchHelper.attachToRecyclerView(binding.mainRecyclerView);
/**
* 逻辑放在Presenter,比如获取数据
*/
mPresenter.initData();
我们的Presenter中定义initData(),并进行具体逻辑,从各种途径获取到我们需要的数据,并且通过接口返回到的Activity:
/**
* 这里进行联网获取数据或者其他逻辑操作
*/
public void initData() {
ArrayList list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
...
/**
* 基类使用了弱引用,每次使用之前应该判空,防止莫名空指针
*/
if (isViewAttached()) {
/**
* Presenter需要MainActivity帮助
*/
getMvpView().setData(list);
}
我们的Activity回调到setData(List list),获取数据并刷新界面
@Override
public void setData(List<String> list) {
data.addAll(list);
adapter.notifyDataSetChanged();
}
大致的调用就是这样一个流程,我们通过Presenter将界面和数据逻辑分离开来,这样我们不管数据是什么获取方式,我们都保证只需要改变initData的业务逻辑就可以了,保证的代码的维护性和整洁。
4.构建基类
public interface MvpView {
}
public interface Presenterextends MvpView> {
/**
* 关联
* @param mvpView
*/
void attachView(V mvpView);
/**
* 解除关联
*/
void detachView();
}
public class BasePresenter<V extends MvpView> implements Presenter<V> {
// private V mMvpView;
protected Reference mMvpView;
@Override
public void attachView(V mvpView) {
this.mMvpView = new WeakReference<>(mvpView);
}
@Override
public void detachView() {
if (this.mMvpView != null) {
this.mMvpView.clear();
this.mMvpView = null;
}
}
/**
* 是否已经关联
*
* @return
*/
public boolean isViewAttached() {
return mMvpView != null && mMvpView.get() != null;
}
/**
* 获取接口
*
* @return
*/
public V getMvpView() {
return mMvpView.get();
}
/**
* 检查是否关联并抛出自定义异常
*/
public void checkViewAttached() {
if (!isViewAttached()) throw new MvpViewNotAttachedException();
}
public static class MvpViewNotAttachedException extends RuntimeException {
public MvpViewNotAttachedException() {
super("Please call Presenter.attachView(MvpView) before" +
" requesting data to the Presenter");
}
}
}
在这里我们使用弱引用,原因在于我们的中间人在实际开发中,我们经常是需要通过联网去获取数据,在我们还在联网获取的数据的时候。我们的视图层也就是我们的Activity可能就已经消亡了,但是我们还是会持有他的引用而造成内存泄露。
public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity implements MvpView{
protected T mPresenter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getBinding();
mPresenter = createPresenter();
mPresenter.attachView(this);
this.initViews(savedInstanceState);
this.initData();
this.initListeners();
}
@Override
protected void onDestroy() {
super.onDestroy();
mPresenter.detachView();
}
/**
* create presenter
* @return
*/
protected abstract T createPresenter();
/**
* databinding setContentView()
*/
protected abstract void getBinding();
/**
* initialize the view in the layout
* @param savedInstanceState
*/
protected void initViews(Bundle savedInstanceState) {
}
/**
* initialize the Activity data
*/
protected void initData() {
}
/**
* initialize
*/
protected void initListeners() {
}
}
强制子类实现获取Presenter,并在Activity的生命周期的onCreate和onDestroy方法中进行关联与解关联。这样基类就构建完成了
5.使用MVP的正确姿势
直接上代码使用流程也都差不多,我习惯是从Activity开始的,有需要才去Prensenter中创建逻辑方法,然后在去定义接口回调到Activity.
Activity代码如下:
public class MainActivity extends BaseActivity implements SwipItemTouchCallback.OnDragListener,MainView {
private MainActivity self = MainActivity.this;
private ActivityMainBinding binding;
private List data;
private MainAdapter adapter;
private ItemTouchHelper itemTouchHelper;
@Override
protected MainPresenter createPresenter() {
return new MainPresenter();
}
@Override
protected void getBinding() {
binding = DataBindingUtil.setContentView(self, R.layout.activity_main);
}
@Override
protected void initViews(Bundle savedInstanceState) {
super.initViews(savedInstanceState);
}
@Override
protected void initData() {
super.initData();
data = new ArrayList<>();
adapter = new MainAdapter(data);
binding.mainRecyclerView.setHasFixedSize(true);
binding.mainRecyclerView.setAdapter(adapter);
binding.mainRecyclerView.setLayoutManager(new GridLayoutManager(self, 4));
// binding.mainRecyclerView.addItemDecoration(new DividerGridItemDecoration(self));
itemTouchHelper = new ItemTouchHelper(new SwipItemTouchCallback(adapter).setOnDragListener(this));
itemTouchHelper.attachToRecyclerView(binding.mainRecyclerView);
/**
* 逻辑放在Presenter,比如获取数据
*/
mPresenter.initData();
}
@Override
protected void initListeners() {
super.initListeners();
binding.mainRecyclerView.addOnItemTouchListener(new OnRecyclerItemClickListener(binding.mainRecyclerView) {
@Override
public void onItemClick(RecyclerView.ViewHolder vh) {
CUtils.showMsg(data.get(vh.getAdapterPosition()));
}
@Override
public void onItemLongClick(RecyclerView.ViewHolder vh) {
if (vh.getAdapterPosition()!=data.size()-1) {
itemTouchHelper.startDrag(vh);
CUtils.Vibrate(self, 70);
}
}
});
}
@Override
public void onFinishDrag() {
CUtils.showMsg(getString(R.string.ok));
}
@Override
public void setData(List list) {
data.addAll(list);
adapter.notifyDataSetChanged();
}
}
MainPresenter中间人的代码如下:
public class MainPresenter extends BasePresenter<MainView> {
/**
* 这里进行联网获取数据或者其他逻辑操作
*/
public void initData() {
ArrayList list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
list.add("f");
list.add("g");
list.add("h");
list.add("i");
list.add("j");
list.add("k");
list.add("l");
list.add("m");
list.add("n");
list.add("o");
list.add("p");
list.add("q");
list.add("r");
list.add("s");
list.add("t");
list.add("u");
list.add("v");
list.add("w");
list.add("x");
list.add("y");
list.add("z");
list.add("...");
/**
* 基类使用了弱引用,每次使用之前应该判空,防止莫名空指针
*/
if (isViewAttached()) {
/**
* Presenter需要MainActivity帮助
*/
getMvpView().setData(list);
}
}
}
这里值得一提的是因为使用的是弱引用,所以我们在每次去获取Activity实例的时候都需要进行一次判空操作。也许会影响性能吧。。。
MainView接口代码
“`
public interface MainView extends MvpView {
/**
* 已经获取到数据,需要MainActivity显示数据
* @param list
*/
void setData(List list);
}
6.结语
后期可能会有修改,一切以github上的代码为最后标准
整个项目的sample地址:https://github.com/jiahuahuang/H-MVP
独自一个人摸索,有不对的欢迎指导