吹吹水
最近刚刚上班,几天连续加班,做重构,满心激情,像打了鸡血一样。连续几天没去健身了,心里痒痒的,今天果断不加班,回来去健身。回到家里看着有些时间,这几天做的东西也有点心得,赶紧写写东西记录一下。
背景
首先,项目重构撸了两三天的网络部分,采用的Retrofit
+RxJava
,这部分后面打算搞个时间再说,然后想到整体架构的时候,我决定采用MVP模式,观摩了google
的MVP-TODO
项目。比较贱的是,我又想加入Dagger2
来做到更加解耦,看到了MVP-TODO
中也有一个分支叫做MVP-Dagger
。word天啊,业界良心!!!
看了之后,里面的套路还是用一个Contract
把View
和Presenter
绑定起来,这样说吧,本来的MVP
就可能会增加很多类,但是这个MVP
+ Dagger2
的规模达到了要做一个页面可能要添加5个类,这个我可不能接受,想了好久,终于让我在phphub-android中找到了解决方案,这里面引用了一个库用于简化MVP,这个库的名字叫做nucleus,这个库采用一种比较巧妙的方法把View和Presenter绑定起来,只需要一个Actiivty
和一个Presenter
即可,不用声明一个Interface
来进行绑定。同时这个库支持RxJava
,我看了一下WIKI
,说的是可以支持APP
异常推出的数据保存和恢复,有兴趣的可以传送过去看看。传送门—-nucleus
好了,吹水吹得够多了,下面是正题。
本人是基于nucleus把里面的核心部分抽出来,放到自己的项目中去。
准备工作
首先我们需要定义一个通用的Presenter
:
public class Presenter<View> {
View view;
public View getView(){
return view;
}
public void setView(View view){
this.view = view;
}
}
很简单,里面就一个View
的泛型
然后我们定义一个PresenterFactory
接口用于创建Presenter
public interface PresenterFactory<P extends Presenter> {
P createPresenter();
}
接下来,耐心看,目前不需要知道为什么要这样做,先跟着看,后面会讲解流程的。
定义一个ViewWithPresenter
接口:
public interface ViewWithPresenter<P extends Presenter> {
PresenterFactory<P> getPresenterFactory();
void setPresenterFactory(PresenterFactory<P> factory);
P getPresenter();
}
还要定义一个注解:
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePresenter {
Class<? extends Presenter> value();
}
定义一个代理类:
public class PresenterDelegate<P extends Presenter> {
private PresenterFactory<P> presenterFactory;
private P presenter;
public PresenterDelegate(@Nullable PresenterFactory<P> presenterFactory) {
this.presenterFactory = presenterFactory;
}
@Nullable
public PresenterFactory<P> getPresenterFactory() {
return presenterFactory;
}
public void setPresenterFactory(@Nullable PresenterFactory<P> presenterFactory) {
this.presenterFactory = presenterFactory;
}
public P getPresenter() {
if (presenterFactory != null) {
if (presenter == null) {
presenter = presenterFactory.createPresenter();
}
}
return presenter;
}
public void onCreate(Object view){
getPresenter();
if(presenter != null){
presenter.setView(view);
}
}
}
定义一个实现了PresenterFactory
的实现类:
public class ReflectionPresenterFactory<P extends Presenter> implements PresenterFactory<P> {
private Class<P> presenterClass;
@Nullable
public static <P extends Presenter> ReflectionPresenterFactory<P> fromViewClass(Class<?> viewClass) {
RequirePresenter annotation = viewClass.getAnnotation(RequirePresenter.class);
//noinspection unchecked
Class<P> presenterClass = annotation == null ? null : (Class<P>)annotation.value();
return presenterClass == null ? null : new ReflectionPresenterFactory<>(presenterClass);
}
public ReflectionPresenterFactory(Class<P> presenterClass) {
this.presenterClass = presenterClass;
}
@Override
public P createPresenter() {
try {
return presenterClass.newInstance();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
}
接着到高潮了,定义一个Activity
,我暂且跟着那个框架命名:
public class NucleusActivity<P extends Presenter> extends AppCompatActivity implements ViewWithPresenter<P> {
private PresenterDelegate<P> presenterDelegate =
new PresenterDelegate<>(ReflectionPresenterFactory.<P>fromViewClass(getClass()));
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
presenterDelegate.onCreate(this);
}
@Override
public PresenterFactory<P> getPresenterFactory() {
return presenterDelegate.getPresenterFactory();
}
@Override
public void setPresenterFactory(PresenterFactory<P> factory) {
presenterDelegate.setPresenterFactory(factory);
}
@Override
public P getPresenter() {
return presenterDelegate.getPresenter();
}
}
嗯,这样就把东西构建完毕了,现在只要要你定义一个BaseActivity
继承NucleusActivity
即可:
public class BaseActivity<P extends Presenter> extends NucleusActivity<P> {
}
假设我有一个MainActivity
和一个MainPresenter
的话,要这样写:
@RequirePresenter(MainPresenter.class)
public class MainActivity extends BaseActivity<MainPresenter> {
}
这样写了之后基本上就可以绑定View
和Presenter
了,为什么会这样呢。
我们来撸一撸代码:
我们在NucleusActivity
里面的onCreate
方法中调用了PresenterDelegateonCreate
方法。我们具体看看:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
presenterDelegate.onCreate(this);
}
再看回这个delegata
是什么东西:
presenterDelegate =
new PresenterDelegate<>(ReflectionPresenterFactory.<P>fromViewClass(getClass())
这个代理类代理的是ReflectionPresenterFactory.<P>fromViewClass(getClass())
这个PresenterFactory
@Nullable
public static <P extends Presenter> ReflectionPresenterFactory<P> fromViewClass(Class<?> viewClass) {
RequirePresenter annotation = viewClass.getAnnotation(RequirePresenter.class);
//noinspection unchecked
Class<P> presenterClass = annotation == null ? null : (Class<P>)annotation.value();
return presenterClass == null ? null : new ReflectionPresenterFactory<>(presenterClass);
}
当我们在MainActivity
上使用的@RequirePresenter
的时候,我们就可以在这里得到我们声明的Presenter
的class
对象,然后createPresenter
的实现如下:
@Override
public P createPresenter() {
try {
return presenterClass.newInstance();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
可以看到,直接就是class.newInstance()
出来的Presenter
再看回PresenterDelegate
的onCreate
方法:
public void onCreate(Object view){
getPresenter();
if(presenter != null){
presenter.setView(view);
}
}
首先调用了getPresent()
用我上面说的方法创建了Presenter
,然后,调用了setView
方法,这个View
其实就是MainActivity
的实例。这样Presenter
就持有了View
的对象。P和V已经绑定。
V 和 P 是什么时候绑定的呢?
我们知道,Activity
实现了ViewWithPresenter
接口,里面就有一个getPresenter
方法,这个方法调用的就是PresenterDetagate
的getPresenter()
方法
@Override
public P getPresenter() {
return presenterDelegate.getPresenter();
}
delegate.getPresenter()
的实现:
public P getPresenter() {
if (presenterFactory != null) {
if (presenter == null) {
presenter = presenterFactory.createPresenter();
}
}
return presenter;
}
这样的话,我们在MainActivity
中通过getPresenter
就可以获取Presenter
的实例,而在Presenter
中调用了getView
就能获取MainActivity
的实例。实现了双向绑定。
总结
可能我上面说的逻辑有点绕,也说得不太明白。不过这个东西本来就有点绕,把代码打出来,看多几遍就自然能理解它的工作流程。主要我是觉得,这种方案能减少很多不必要的类。值得借鉴参考。
注意
我在实践的过程中,发现Activity
其实还存在两个方法getPresenterFacotry()
和setPresenterFacory()
,默认的Activity
是在onCreate
的已经使用代理类创建了Presenter
,所以如果你需要自定义一个PresenterFacotry
来替代默认的话,需要在onCreate
之前(在BaseActivity
的super.onCreate()
之前调用即可),这个其实是在onCreate
的时候已经使用PresenterFactory
创建了Presenter
,并且已经实现了绑定,所以你再使用那两个方法的话,得到的不是同一个实例。
我遇到这个坑是因为我添加了Dagger2
,需要注入Presenter
所以遇到这个问题(其实Nucleus
里面是在onResume
方法里面进行绑定P和V的,但是我们开发习惯是通常在onCreate
的时候就有可能使用到Presenter
,为了防止空指针,我放在了onCreate
里面,至于Nucleus
是如何处理这个问题,我没有深入去理解过)