前言:Android中使用MVP开发已经很普遍了,网上也已经有好多种MVP模式的文章和设计。但是一直没能达到想要的效果。之前一直采用的 一个页面对应一个presenter(class)和一个view(interface)的模式,但是这种模式遇到页面业务逻辑过多的时候,p层和v层的代码量会特别大。现在说一下根据这个模式改进后,达到单个页面实现多个presenter。方便多种业务解耦,让单个文件代码量不至于过大的方法。
防止看代码太费劲。
直接放链接:https://github.com/hanswook/mvpdemo
一、按照google的sample中contract(契约类)模式会存在一些内存泄漏问题(长时间的操作在页面销毁后,p层持有v层导致页面无法被回收)。变种产生了basepresenter中进行view和presenter的绑定解绑操作。如下代码所示
public abstract class BasePresenter<V> {
public WeakReference<V> mViewRef;
protected Reference<Context> mContextRef;
public void attachModelView(V pView,Context context) {
mViewRef = new WeakReference<V>(pView);
this.mContextRef = new WeakReference<>(context);
}
//...省略中间部分
public void onDettach() {
if (null != mViewRef) {
mViewRef.clear();
mViewRef = null;
}
if (mContextRef != null) {
mContextRef.clear();
mContextRef = null;
}
}
BaseView
public interface BaseView {
//做一些统一的方法处理
}
BaseActivity
public abstract class BaseActivity<V, T extends BasePresenter<V>> extends AppCompatActivity implements BaseView{
protected T mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
mPresenter = createPresenter();
if (mPresenter != null) {
mPresenter.attachModelView((V) this, this);
}
mActivity = this;
init();
}
protected abstract int getLayoutId();
protected abstract void init();
/**
* 使用MVP模式时需要重写该方法
* @return
*/
protected T createPresenter() {
return null;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.onDettach();
}
}
}
页面代码(伪)
public class HomeActivity extends BaseActivity<IHomeView, HomePresenter>
implements IHomeView{
}
通过泛型,指明类型。一个页面绑定一个presenter 实现一个view。统一在base里面处理presenter的绑定解绑,避免内存泄漏,达到mvp的效果。
二、改进,上面的模式虽然可以实现mvp,但是当页面逻辑过多时,P层代码增长速度会急剧上升。所以考虑之后,决定一个页面可以有多个presenter,实现多个view。将不同的业务解耦,放在不同的presenter中,如果粒度合理,甚至可以同一套presenter复用到多个需要这种业务的页面上。
话不多所,直接放代码。
base类 ,这是三个文件。
public interface IBaseView {
}
public interface IBasePresenter<V extends IBaseView> {
/**
* 绑定view到presenter
*
* @param pView
* @param context
*/
void onAttach(V pView, Context context);
/**
* 解绑view
*/
void onDettach();
}
public abstract class BasePresenter<V extends IBaseView> implements IBasePresenter<V> {
public WeakReference<V> mViewRef;
protected Reference<Context> mContextRef;
@Override
public void onAttach(V pView, Context context) {
mViewRef = new WeakReference<V>(pView);
this.mContextRef = new WeakReference<>(context);
}
// ....省略部分代码
@Override
public void onDettach() {
if (null != mViewRef) {
mViewRef.clear();
mViewRef = null;
}
if (mContextRef != null) {
mContextRef.clear();
mContextRef = null;
}
}
}
BaseActivity 省略了部分代码。完整的可以看链接。
public abstract class BaseActivity extends AppCompatActivity implements IBaseView {
protected List<IBasePresenter> mPresenters;
protected AppCompatActivity mActivity;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
mPresenters = new ArrayList<>();
mActivity = this;
addPresenters();
for (IBasePresenter presenter : mPresenters) {
presenter.onAttach(this, mActivity);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
for (IBasePresenter presenter : mPresenters) {
presenter.onDettach();
}
}
protected abstract void addPresenters();
protected void addToPresenters(IBasePresenter child) {
mPresenters.add(child);
}
这里我们模拟两个业务,一个登录模块的获取名称,一个新闻模块的获取新闻列表。
public interface LoginContract {
interface IView extends IBaseView {
void showName(String name);
}
interface Presenter extends IBasePresenter<IView> {
void getName();
}
}
public interface NewsContract {
interface IView extends IBaseView {
void showNews(List<String> news);
}
interface Presenter extends IBasePresenter<IView> {
void getNews();
}
}
public class LoginPresenter extends BasePresenter<LoginContract.IView> implements LoginContract.Presenter {
@Override
public void getName() {
if (isAttach()) {
getView().showName("小刚");
}
}
}
public class NewsPresenter extends BasePresenter<NewsContract.IView> implements NewsContract.Presenter {
@Override
public void getNews() {
if (isAttach()) {
List<String> news = mockNews(); //模拟一些数据返回,方法省略
getView().showNews(news);
}
}
}
测试页面代码
public class MainActivity extends BaseActivity implements LoginContract.IView, NewsContract.IView {
private LoginContract.Presenter loginPresenter;
private NewsContract.Presenter newsPresenter;
@Override
protected void addPresenters() {
loginPresenter = new LoginPresenter();
newsPresenter = new NewsPresenter();
addToPresenters(loginPresenter);
addToPresenters(newsPresenter);
}
@Override
protected int getLayoutId() {
return R.layout.activity_main;
}
@Override
protected void init() {
}
public void testLogin(View view) {
loginPresenter.getName();
}
public void testNews(View view) {
newsPresenter.getNews();
}
@Override
public void showName(String name) {
Toast.makeText(mActivity, "name:" + name, Toast.LENGTH_SHORT).show();
}
@Override
public void showNews(List<String> news) {
Toast.makeText(mActivity, "news一共有" + news.size() + "条消息", Toast.LENGTH_SHORT).show();
}
}
在基类中统一处理presenter,和上面逻辑类似,只是将单个presenter换成list,挨个绑定和解绑。basepresenter抽象类实现presenter接口。采用契约类的模式约束业务。业务与业务之间解耦,有几种业务,就产生几种presenter。
三、数据层做了统一的封装,全局单例获取统一的实例。无论是网络 内存 数据库,统一将数据分业务放到一起去处理。这里p层中并没有模拟出model的代码,看似mvp中只有vp,没有了m。 只是因为这篇记录主要记录关于vp的改动。model层并没有太大修改。经过统一封装了之后,在presenter的init方法中获取实例并操作。 故此处省略的model层的代码。
仅此记录开发中的一些改进。欢迎勘误。多谢~