Android MVP架构浅析(续)

  继前篇博文对MVP架构做了简单解析之后遗留的问题还是挺多的,所以接下来的几篇我会慢慢重构它,尽量避免由于梯度过大而使初学者产生疑惑。关于架构设计我没有比较深刻的理解,经过几个项目的实战后,我总结了一句话:所谓设计,即在变与不变间斟酌;您是否从这句话看出了面向对象设计的理念---抽象,我觉得你是可以领悟到的,否则你就不会阅读本文了,那我们今天就先从变与不变开始吧。上篇文章中有段代码,不知你还否记得:

/**
 * Created by 小雨 on 2015/11/15.
 */
public class FeedPresenter {

    private FeedView mFeedView;

    public FeedPresenter(FeedView feedView) {
        this.mFeedView = feedView;
    }

    public void loadFeedList() {
        this.mFeedView.showFeedList(FeedDataStoreFactory.getInstance().getFeedStoreData());
    }
}

  我们为View创建一个接口是毋容置疑的,但是Presenter拿到View句柄的方式是可以优化的,既然是面向接口编程,那么当前Presenter的依赖对象(View)注入形式是有问题的,已具体到功能模块,这是我们最不愿看到的。插入一段似乎不太合适的话,项目的包组织结构问题,有人喜欢按组件分,有人喜欢按功能分,不管最终以什么维度来划分,意向都是一样的,我们都希望代码结构看着整洁,那就从包结构着手吧,就像重构后的demo,我对包结构做了下调整,上层按组件分,下层按功能分,别问我为什么这么分,因为我喜欢...... 我还是稍作解释一下吧,不然有些同学肯定会打我的! 数据层(data)向表示层(presentation)提供数据,presentation并不关注data从哪里拿到的数据,DataStoreFactory负责具体执行,cache 、net or disk,这些都是data的职责,作为表示层我只负责展示就好了,不该我管的事我不管。presentation按功能模块划分包组织结构,基层构建放在根包下,其它按具体功能划分。

回到刚才Presenter依赖注入方式的问题。问题的本质就是我们把实现耦合了---多么痛的领悟... 应该依赖于抽象而不是依赖于具体! 所以先从接口抽象重构。 View的接口抽象定义:

/**
 * Created by 小雨 on 2015/11/15.
 */
public interface MvpView {
}

如你所见,MvpView接口并没有定义任何行为方法,它的作用就是把View从具体功能中抽象出来。

像刚才说的那样,Presenter的依赖应该被抽象出来,得到和失去本是天生一对,Presenter接口的抽象定义:

/**
 * Created by 小雨 on 2015/11/15.
 */
public interface MvpPresenter<V extends MvpView> {

    void attachView(V view);

    void detachView(boolean retainInstance);
}

Presenter接口定义好了,该如何管理View的句柄呢(何时注入? 何时销毁?),我们不可能把这些交给具体业务实现中去做吧,这样就违背了设计的初衷了Presenter持有View句柄的方式我们依然需要抽象出来,所以就有了下面这段代码:

/**
 * Created by 小雨 on 2015/11/15.
 */
public abstract class MvpBasePresenter<V extends MvpView> implements MvpPresenter<V> {

    private V mView;

    @Override
    public void attachView(V view) {
        this.mView = view;
    }

    protected V getView() {
        return this.mView;
    }

    protected boolean isViewAttached() {
        return this.mView != null;
    }

    @Override
    public void detachView(boolean retainInstance) {
        this.mView = null;
    }
}

需要注意一点的是必须在onDestroy()中调用detachView释放与view解绑。

Presenter管理View句柄的问题已经解决,那View如何持有Presenter的句柄呢,代码如下:

/**
 * Created by 小雨 on 2015/11/15.
 */
public abstract class MvpBaseActivity<P extends MvpPresenter> extends AppCompatActivity implements MvpView {

    protected P presenter;

    protected abstract P createPresenter();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        presenter = createPresenter();
        if (presenter == null) {
            throw new NullPointerException("Presenter is null! Do you return null in createPresenter()?");
        }
        presenter.attachView(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter.detachView(false);
    }
}

MvpBaseActivity管理着View何时被注入到Presenter中,又是何时从Presenter中移除的。createPresenter()作为一个钩子延迟到子类去实现,多态的表现。

重构后的抽象基础构件已经定义好,现在时候是时候派上用场了,重构后的FeedPresenterImpl需要继承MvpBasePresenter,同时实现FeedPresenter接口即可

/**
 * Created by 小雨 on 2015/11/15.
 */
public class FeedPresenterImpl extends MvpBasePresenter<FeedView> implements FeedPresenter {

    public FeedPresenterImpl() {
    }

    @Override
    public void loadFeedList() {
        if (isViewAttached()) {
            getView().showFeedList(FeedDataStoreFactory.getInstance().getFeedStoreData());
        }
    }

}   

因为Presenter仅以弱引用形式持有View句柄,所以每次使用前View需要判定View是否已经被销毁掉。

和FeedPresenterImpl类似,FeedActivity需要继承MvpBaseActivity,同时实现FeedView接口,具体实现代码:

/**
 * Created by 小雨 on 2015/11/15.
 */
public class FeedActivity extends MvpBaseActivity<FeedPresenter> implements FeedView {

    private ListView vFeedListView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        vFeedListView = (ListView) findViewById(R.id.feed_list);

        init();
    }

    private void init() {
        presenter.loadFeedList();
    }

    @Override
    public void showFeedList(List<Feed> feedList) {
        vFeedListView.setAdapter(new FeedAdapter(feedList));
    }

    @Override
    protected FeedPresenter createPresenter() {
        return new FeedPresenterImpl();
    }
}

至此,我们的重构告一段落了,也许你对本次重构稍有疑惑,但我相信你多看一遍就会理解。如一开始所说,本文只是重构的一环,所以你的持续关注是我前进的动力哈。

实例源码github地址:https://github.com/Tiny-Times/android-mvp2

发表评论

电子邮件地址不会被公开。 必填项已用*标注