MVP中Presenter复用思路和Inject-mvp

MVP模式早已经不是什么新鲜词了,这里不再赘述。最近在重构代码的过程中,发现了一件及其痛苦的事情:很多时候,model层在应用中是很薄的,大多数的业务逻辑都在Presenter层,但是由于模版 代码,Activity(View)->P是一一对应绑定的关系,那这样,相当于只是把原来在Activity中的逻辑转移到了Presenter中,虽然一定程度上解耦了,方便单测了,但是事实上Presenter很难复用。先贴一下使用的MVP模版类:
BaseMvpActivity.java


/**
 * MVP模式的Activity基类
 */
public abstract class BaseMvpActivity<P extends BasePresenter> extends Activity{

/**
 * 使用Dagger2 注入presenter
 */
    @Inject
    protected P presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        injectMethod();
        if (presenter != null) {
            presenter.attachView(this);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (presenter == null) {
            return;
        }
        presenter.detachView();
    }

    /**
     * 获取Presenter对象
     */
    protected P getPresenter() {
        return presenter;
    }

/**
 * 子类利用Dagger注入
 */
    protected abstract void injectMethod();
}

BaseView.java

/**
 * MVP模式的View基类
 */
public interface BaseView {
    //绑定lifecircle,防止内存泄漏以及空指针
    <T> AutoDisposeConverter<T> bindAutoDisposable();
}

BasePresenter.java

public abstract class BasePresenter <V extends BaseView>{

    private WeakReference<V> viewRef;

    public void attachView(V view) {
        viewRef = new WeakReference<>(view);
    }


    public void detachView() {
        if (viewRef != null) {
            viewRef.clear();
            viewRef = null;
        }
    }

    protected V getView() {
        return viewRef == null ? null : viewRef.get();
    }
}

这个模版是MVP使用比较多的一中,可以看到BaseMVPActivity中只有一个presenter,这意味着一个继承了它的Activity只能有这一个地方处理逻辑。

举个例子,应用中A页面有几个action是检查权限、接单,B页面是请求配置项、检查权限、更新个人信息。这两个页面都有“检查权限”这个action,那么代码的写法可能是:
APresenter.java

public class APresenter extends BasePresenter<IAView>{
	public void checkAuth(){
		// 检查权限
		getView().checkResult();
		...
	}
	public void acceptOrder(){
		// 接单
		getView().acceptOrderSuccess();
		...
	}
}

BPresenter.java

public class BPresenter extends BasePresenter<IBView>{
	public void checkAuth(){
		// 检查权限
		getView().checkResult();
		...
	}
	public void requestConfig(){
		// 请求配置项
		...
	}
	public void updateMyInfo(){
		//更新个人信息
		...
	}
}

这两个Presenter中都有checkAuth()这个方法,但是却没办法抽出来复用,因为我们的模版代码中,用泛型指定了Presenter只能对应一个View,一个View反过来也只能对应一个Presenter。

这样的代码并不灵活,也很冗余。当几个页面公共逻辑比较多的时候,只能copy代码。

MVP模版做了什么?

要解决presenter不能复用的问题,首先要知道为什么Presenter被绑死了。BaseMVPActivity要做的事情,是提供一个presenter成员,并管理presenter和activity的绑定关系,但是presenter成员并不是它提供的,真正实例这个presenter的是它的子类,所以***BaseMVPActivity只有指定泛型来告诉子类应该实例什么类型的Presenter***。如果想要多个Presenter同时用这种方法实例,BaseMVPActivity是不支持的,因为它只能指定一个泛型。

注意加粗和斜体的字,既然由于BaseMVPActivity是因为要做这么一件事,导致Presenter和Activity綁死,那么不用泛型,由实际的Activity自己去声明、实例化和管理presenter是不是就可以了?

当然可以了!因为这就是MVP最初最干净的模型(伪代码):

 public class AActivity extends Activity{
    	PresenterA  presenterA = new PresenterAImpl();
    	PresenterCheckAuth presenterCheckAuth = new PresenterCheckAuthImp();
    	....
    	onCreate(){
    		presenterA.attatchView(this);
    		presenterCheckAuth.attatchView(this);
    	}
    	onDestroy(){
    		presenterA.dettachView();
    		presenterCheckAuth.dettachView();
    	}
    }

这样当然没问题,但是每次要使用MVP,都要声明、实例化、管理绑定关系,这些活儿太繁琐了,这也是为啥要有模版代码BaseMVPActivity这个东西。

这时候好像绕了一圈绕回去了。

解决思路

毫无疑问,要有个东西可以替代BaseMVPActivity的管理作用,但是它不应该将presenter和activity綁死。有没有办法,把上面onCreate和onDestroy里面的东西代替了?
想到用编译时注解,生成如下的代码:

public class AActivity_BinderWrapper implements BinderWrapper<AActivity> {
  @Override
  public void bindMember(final AActivity target) {
    target.presenterA.attachView(target);
    target.presenterCheckAuth.attachView(target);
  }

  @Override
  public void unbind(final AActivity target) {
    target.presenterA.detachView();
    target.presenterCheckAuth.detachView();
  }
}

然后在onCreate和onDestory处调用AActivity_BinderWrapper的bindMember(final AActivity target) 和
unbind(final AActivity target) 。
有注解框架生成代码,自然比自己手动写要省事了。可是还是有问题啊,还是需要手动在onCreate和onDestory处调用bindMember(final AActivity target) 和
unbind(final AActivity target)啊,无非是以前可能要attach 和 detach很多次,现在只要一次而已,但是仍然需要手动干预。

进一步演进

AActivity_BinderWrapper 的bindMember(final AActivity target) 和unbind(final AActivity target)如果放到基类中,这个事情不就解决了?但是麻烦的是,在基类中的bindMember和unBind方法,并不知道当前自己的实例到底是谁。比如把它们放在了一个叫BaseNewMVPActivity的类里,有AActivity和BActivity继承了它,只有运行的时候,才能知道,要bindMember或者unBind的类是AActivity还是BActivity。编译期没办法简单的用bindMember(this) 和unbind(this)达到效果。

怎么办呢?既然运行时才知道BaseNewMVPActivity的实例是谁,那我运行时再bindMember和unBind呗?那编译期要做什么呢?那就是确定运行时要用哪个BinderWrapper。依然利用编译时注解,生成如下代码:

public final class ActivityBinder implements AndroidBinder<Activity> {
  private HashMap<Class<? extends Activity>, BinderWrapper<? extends Activity>> classBinderWrapperHashMap;

  private BinderWrapper<AActivity> bindWrapper_AActivity;
  private ActivityBinder() {
    classBinderWrapperHashMap = new HashMap<>();
    bindWrapper_AActivity = new AActivity_BinderWrapper();
    classBinderWrapperHashMap.put(AActivity.class,bindWrapper_AActivity);
  }

  public static ActivityBinder create() {
    return new ActivityBinder();
  }

  @Override
  public void bind(Activity instance) {
       BinderWrapper<Activity> wrapper = (BinderWrapper<Activity>) classBinderWrapperHashMap.get(key);
       if(wrapper != null){
           wrapper.bindMember(instance);
       }
    }}

  @Override
  public void unbind(Activity instance) {
    BinderWrapper<Activity> wrapper = (BinderWrapper<Activity>) classBinderWrapperHashMap.get(key);
       if(wrapper != null){
            wrapper.unbind(instance);
        }
    }}
}

这里用一个HashMap存储对应关系,用Class作为key,对应的BindWrapper作为value,这是在编译期生成的,运行时,拿到当前Activity的class,从HashMap中拿出对应的BindWrapper,进行绑定和解绑。

到这里,在基类Activity中管理Presenter的绑定就解决了。

more

上面这些仅仅是解决了问题,但是如果每个Activity都去持有一个ActivityBinder,显然是不合适的,因为这样会持有很多和它无关的BindWrapper,导致内存的浪费。更优雅的,将ActivityBinder让Application持有,成为全局的,然后activity.getApplication()拿到BindWrapper,在对应的onCreate和onDestroy中使用。

此外,这里生成的代码只是对presenter做了attach和detach的操作,并没有做IOC的工作,因为Dagger2已经是很优秀的轮子了。配合Dagger2,可以简化很多工作。只要你使用注解的类有attach和detach的方法,就可以用这个小工具了。

demo 地址:https://github.com/yueshaojun/inject-mvp.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值