关于Android 的MVP的理解

现在主流都采用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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值