关于Android 的MVP的理解

来源:互联网 发布:实体转json忽略空值 编辑:程序博客网 时间:2024/05/07 06:01

现在主流都采用MVP的模式的搭建android的项目,包括连谷歌也在前段时间发布了几个关于mvp模式的写法,其中包括和rxjava和dragger2的融合的项目。所以是时候换一种方式写android的项目

写在前面的话

传统的mvc的模式,写android的项目,随着业务的扩展会使得c层(Activity)的代码变得臃肿和难以维护,如果你只是为了将原先的属于的c层的代码移除到p层,那么只是换汤不换药,只不过原来臃肿的代码换到了p层。所以在之前,我们一定要明白,分层的概念。 关于android的分层的概念其实就是低耦合的具体的表现。
参考:
1. Android项目重构之路:架构篇
2. Android项目重构之路:界面篇
3. Android项目重构之路:实现篇

mvp的模式

最简单的模型图

这里写图片描述

View: 负责加载视图,更新视图和隐藏视图,总之View管理着视图的所有操作。
Presenter:负责事的逻辑处理,并且根据处理的结果去更新View层和保存Model层
Model:这里的model不是单一的bean的对象,其中包括了data和domain。下面的图表明了三者包涵部分以及协作关系。

这里写图片描述

针对每层具体的描述

这里以点击登录去登录的过程来分析。

View 层

这层属于整个体系最简单,主要就是用来负责视图操作的,当我们拿到设计需求的时候,就大概知道每个页面的大概会展示的时候会存在的操作,比如loadList(),showError(),loadProgess(),等一些操作。而将所有的视图的操作都给注入进来的presenter去操作。比如这个点击登录按钮,在监听的操作操作,交给presenter去处理。等待presenter的处理完结果的通知页面的操作即可。

另外现在的Data Binding 可以进一步简化View 的代码,关于这方面的学习可以
Data Binding 用户指南(Android)

Presenter

我把这层的想象成调控层,就是接受从View层的信息,从model层去model,然后通过callback的形式去监听model处理完逻辑后,从而通知更新视图和更新数据等。是一个集disaptcher和deliver的中间层。比如登录过程,它接受View传过来的点击事件,然后取到一个user,调用user.login()的方法,并通过监听的方式去得到成功和失败的结果。并根据结果去通知view的去改变视图。

这一块可以实用RxJava,和RxAndroid,从而解决回调嵌套回调的问题。

Model层

个人觉得是整个框架的最难的地方,第一其必须向上隐藏实现的过程和实现细节,第二向下能够满足业务的需求。其中包括大致分为DataManager和interactor两个部分。

DataManager:大致可以分为Local和remote,其中实现必要的添,删,改,查等操作。

其中Local 可以分为- DB: 用的最多- DisK : 一般采用LurDiskCache其中remote,通过api的方式得到数据- Retrofit- Volley- OkHttp

interactor

具体的逻辑操作,比如登录,user.login().

对于这一块可以采用工厂模式向上提供model,供presenter去操作。并添加相应的改,删,增的操作去保存数据。

项目的例子

CreateNewAndroid

项目的简单说明

项目就是做一个简单的splashActivity,用来展示splashImage的。接口是来自开源的知乎日报的。

其中SplashContract中声明了view和presenter的所有的方法。

public interface SplashContract {    interface View {        void displayImage(String imageUrl);        void loading();        void showErrorMessage(String message);    }    interface Presenter {        void requestImageUrl();        void attach(View view);    }}

而在真正实现的类中用来都是用接口来接偶的。

真正实现的view层,只有视图的相关的操作。

public class SplashActivity extends AppCompatActivity implements SplashContract.View {    private ImageView mSplashImage;    private ProgressBar mLoadingBar;    private SplashContract.Presenter mPresenter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_splash);        mSplashImage = (ImageView) findViewById(R.id.iv_splash);        mLoadingBar = (ProgressBar) findViewById(R.id.pb_splash_loading);        mPresenter = new SplashPresenter.SplashPresenterFactory().newSplashPresenterInstance();        mPresenter.attach(this);        mPresenter.requestImageUrl();    }    @Override    public void displayImage(String imageUrl) {        mLoadingBar.setVisibility(View.GONE);        mSplashImage.setVisibility(View.VISIBLE);        Glide.with(this)                .load(imageUrl)                .asBitmap()                .into(mSplashImage);    }    @Override    public void loading() {        mLoadingBar.setVisibility(View.VISIBLE);        mSplashImage.setVisibility(View.GONE);    }    public void showErrorMessage(String message) {        mSplashImage.setVisibility(View.GONE);        mLoadingBar.setVisibility(View.GONE);        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();    }}

真正的presenter层,采用rxjava和lambda表达式来写的,其中逻辑操作交给SplashImage去执行。SplashImage是一个抽象,对于presenter层来说,不需要做的SplashImage的具体实现类是什么,我们可以告诉其我们需要什么,通过SplashImageFactory来生成一个我们想要的一个SplashImage的对象。

public class SplashPresenter implements SplashContract.Presenter {    private SplashContract.View splashView;    private SplashImage splashImage;    public SplashPresenter() {        splashImage = new SplashImage.SplashImageFactory().newInstance(SplashImage.DISPLAY_SHOW_LOCAL_IF_HAVE);    }    @Override    public void requestImageUrl() {        splashImage.getSplashImageBean()                .doOnSubscribe(() -> splashView.loading())                .observeOn(AndroidSchedulers.mainThread())                .subscribeOn(Schedulers.io())                .subscribe(imageBean -> {                            splashView.displayImage(imageBean.getImg());                            splashImage.saveSplashImageBean(imageBean);                        }                        , throwable -> splashView.showErrorMessage(throwable.getMessage()));    }    @Override    public void attach(SplashContract.View view) {        splashView = view;    }    public static class SplashPresenterFactory {        public SplashPresenter newSplashPresenterInstance() {            return new SplashPresenter();        }    }}

而Model层,方法有添,删,改,查等一系列操作,其中实现类RemotoSplashImage(借助Retrofit来实现)和LocalSplashImage(借助SharePreference来实现的),而具体提供什么的样SplashImage的是根据业务的需求来选择的,这里选择的是如果本地存在,就加载到本地,如果本地没有则加载网络的。

public interface SplashImage {    int DISPLAY_ONLY_SHOW_NET = 0;    int DISPLAY_SHOW_LOCAL_IF_HAVE = 1;    Observable<SplashImageBean> getSplashImageBean();    Observable<Boolean> saveSplashImageBean(SplashImageBean bean);    Observable<Boolean> deleteSplashImageBean(SplashImageBean bean);    Observable<SplashImageBean> updateSplashImageBean(SplashImageBean bean);    class SplashImageFactory {        public SplashImageFactory() {        }        public SplashImage newInstance(int priority) {            switch (priority) {                case DISPLAY_SHOW_LOCAL_IF_HAVE:                    if(!TextUtils.isEmpty(SharePreferenceManager.getsPreferencesManager(NewAndroidApplication.getApplication()).getLocalSplashImageUrl())){                        return new LocalSplashImage();                    }                default:                case DISPLAY_ONLY_SHOW_NET:                    return new RemotoSplashImage();            }        }    }}

运行后结果显示
这里写图片描述

而且根据log的可以看到,确实是第一次加载过后保存在本地就没有请求网络。而是加载本地的
第一次进入的log
这里写图片描述
之后进入的log
这里写图片描述

详细项目见CreateNewAndroid。

如有改进和指错的地方还请大家指出来。谢谢大家

1 0