美文网首页
Android开发—MVPO框架模式(一)

Android开发—MVPO框架模式(一)

作者: GoMoon | 来源:发表于2019-05-26 12:34 被阅读0次

前言

  在淡到Android开发的框架模式时,不外乎MVC、MVVM和MVP三种。而今天,题主想介绍一种新的框架模式——MVPO。这是题主在MVP的基础上扩展出来的一种模式,新的模式当然是为了解决新的问题,而本人相信,在Android开发中困扰着题主的一些问题,同样也困扰着大家。

MVP存在的缺陷

  1. View和Presenter对应关系引发的问题。
      我们往往一个View(Activity或Fragment)对应一个Presnter,然后Presenter里面封装了若干个网络请求和结果处理。那么问题来了,某个网络请求可能会在不同的Presenter间重复。比如有个接口”获取短信验证码“,我们在RegisterPresenter(注册)和ResetPwdPresenter(重置密码)中都存在一模一样的代码,但这两个Presenter又无法整合成一个,因为它们分别对应不同的功能页面,这两个页面除了获取验证码,其它操作都可能是不同的,强行整合成一个Presenter显然也不合理。
      由此,我们遇到了第一个因扰我们的问题:多个Presenter间可能存在重复的代码,却无法优化。

  2. MVP无法满足越来越复杂的App开发。
      在开发单机App(没有网络请求的App)时代,我们觉得MVC很好用。但现在很少有单机App,很多App靠非常多且复杂的网络请求来支撑视图的展示,于是我们用MVP,通过Presenter把网络请求分离出来,同时处理一些存取数据等辅助操作,目的就是为了让View层更轻更薄。然后到了现在,当App变得更加复杂时,比如题主最近开发的App,一个交互中往往穿插有网络请求和蓝牙操作,于是,为了处理这些复杂的操作,我们只得在Presenter中写入更多的逻辑,P层开始多了很多网络请求以外的重要逻辑。
      由此,我们遇到了第二个困扰我们的问题:当开发中夹杂着网络请求及其它同等重要的操作时,P层又会变得臃肿起来。

解决之道

  上面提到了两个问题,那么解决这两个问题,就是题主写这两篇文章的目的。在第一篇文章中,我不会直接讲MVPO,而是先针对第一个问题,通过改进MVP来提出解决方案。而改进后的MVP,本身又会被用到后面的MVPO模式中,因此如果有看官比较急,不妨跳过这一章,等待下个一篇章。
  好了,我们先看看如何解决第一个问题:多个Presenter间存在重复的代码时,该如何优化?

更合理的View和Presenter对应方式

  对此,题主的解决办法是:一个Presenter不再对应一个View,而是对应一个功能模块。比如有三个页面:登录、注册、重置密码。按传统的MVP会有三个Presenter(LoginPresenter、RegisterPresenter、ResetPwdPresenter),现在通通都用同一个Presenter(AccountPresenter)取而代之。
  可能有些看官要打脸了,那你这个AccountPresenter岂不是包含了三个页面的所有网络请求,虽然的确不会再有冗余和重复的代码,但一来,这个AccountPresenter也太不专一且臃肿了;二来,这个Presenter还怎么跟专属的某个Activity对应?
  废话不多说,还是直接上代码吧。

AccountPresenter

    /**
     * 登录
     */
    public static void login(final String mobile, String pwd, BaseObserver<LoginResult> observer) {
        observer.addParam("mobile", mobile)
                .addParam("password", pwd)
                .post(AppConfig.UrlConfig.LOGIN);
    }

    /**
     * 发送短信验证码
     */
    public static void snedSmsCode(String mobile, String sign, String imageCode, BaseObserver observer) {
        observer.addParam("mobile", mobile)
                .addParam("image_code", imageCode)
                .post(AppConfig.UrlConfig.FETCH_SMS_CODE);
    }

    /**
     * 注册新用户
     */
    public static void register(String mobile, String smsCode, String pwd, BaseObserver observer) {
        observer.addParam("mobile", mobile)
                .addParam("sms_code", smsCode)
                .addParam("password", pwd)
                .post(AppConfig.UrlConfig.REGISTER);
    }

    /**
     * 重置密码
     */
    public static void resetPwd(String mobile, String smsCode, String pwd, BaseObserver observer) {
        observer.addParam("mobile", mobile)
                .addParam("sms_code", smsCode)
                .addParam("password", pwd)
                .post(AppConfig.UrlConfig.FORGET_PASSWORD);
    }

是不是有点反常识,我们居然把登录、注册、重置密码三个页面的所有网络请求都整合进同一个Presenter里,也就是说,我们三个Activity都可以用同一个Presenter,而且还能一一对应,不信接着看代码

LoginActivity

    private void doLogin() {
        AccountPresenter.login(mMobile, mPwd, new BaseObserver<LoginResult>(this) {
            @Override
            public void onSuccess(LoginResult loginResult) {
                startActivity(new Intent(LoginActivity.this, MainActivity.class));
                finish();
            }

            @Override
            public void onError(String code, String msg) {
                super.onError(code, msg);
            }
        });
    }

RegisterActivity

    private void doRegister() {
        AccountPresenter.register(mPhone, mSMSCode, mPwd, new BaseObserver(this) {
            @Override
            public void onSuccess(Object o) {
                ToastUtil.show("注册成功");
            }

            @Override
            public void onError(String code, String msg) {
                super.onError(code, msg);
            }
        });
    }

ResetPwdActivity

    private void doResertPwd() {
        AccountPresenter.resetPwd(mPhone, mSMSCode, mPwd, new BaseObserver(this) {
            @Override
            public void onSuccess(Object o) {
               ToastUtil.show("重置密码成功");
            }

            @Override
            public void onError(String code, String msg) {
                super.onError(code, msg);
            }
        });
    }

讲解
  可能有些看官已经发现了,Presenter里面的方法都是静态的,每个Activity按需调用其中某个或若干个方法即可。这种情况下,P层有点类似于工具类,不再和特定的View绑死,而是可以服务于多个视图页面。但问题来了,我们习惯让P层帮我们处理好Loading动画,比如网络请求开始时自动开始转菊花,网络请求结束后让Loading消失。最重要的是,我们希望Activity结束时,能自动取消未完成的网络请求。上面提到的这些,题主都是有实现的,不过换了种方式,放到了BaseObserver里处理。
  没错,BaseObserver才是和View层绑定的重要元素。大家可以看到,每次new BaseObserver,构造函数中都会传入this,这个this其实是一个接口(ILoadingView),而我们的Activity继承的BaseActivity本身实现了这个接口,从而建立绑定关系。不妨来看一下这几者的代码。

ILadingView

public interface ILoadingView {
    /**
     * 展示Loading动画
     */
    void showLoadingDialog(String msg);

    /**
     * 让Loading动画消失
     */
    void hideLoadingDialog();

    /**
     * 保存当前页面所有网络请求,在页面结束时以便取消网络请求
     */
    void addNetRequest(Disposable d);
}

BaseActivity

public abstract class BaseActivity extends AppCompatActivity implements ILoadingView {
    protected Dialog mLoadingDialog;
    protected List<Disposable> mRequestList;
    
    @Override
    public void showLoadingDialog(String msg) {
        mLoadingDialog.show(msg);
    }

    @Override
    public void hideLoadingDialog() {
        mLoadingDialog.dismiss();
    }

    @Override
    public void addNetRequest(Disposable d) {
        if (null == mRequestList) {
            mRequestList = new ArrayList<>();
        }
        mRequestList.add(d);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        RetrofitFactory.cancel(mRequestList);
    }
}

BaseObserver

public abstract class BaseObserver<T> implements Observer<BaseResponse> {
    private ILoadingView mLoadingView;

    public BaseObserver(ILoadingView loadingView) {
        this.mLoadingView = loadingView;
    }
    
    @Override
    public void onSubscribe(Disposable d) {
        if (null != mLoadingView) {
            mLoadingView.addNetRequest(d);
        }
        onStart(d);
    }

    public void onStart(Disposable d) {
        mLoadingView.showLoadingDialog("加载中");
    }

    public void onError(String code, String msg) {
        ToastUtil.show(msg);
    }

    public void onFinish() {
        mLoadingView.hideLoadingDialog();
    }
}

最后总结一下吧

  1. 至此基本已经解释清楚了,我们的Activity和Fragment实现了ILadingView这个接口,并且实现了里面的方法,比如showLoading会转菊花,addNetRequest会把网络请求存下来以便页面退出时结束掉。在new BaseObserver时再把实现的ILoadingView传入其中,从而建立绑定关系。
  2. 由于P层绑定视图的任务已经转移到BaseObserver中,因此Presenter可以直接做成静态工具类,从而可以服务于多个页面,而不会相互干扰。

未完待续

回到开篇提的第一个问题:多个Presenter间存在重复的代码时,该如何优化?通过以上改进后的MVP框架,我们很好解决了这个问题,让Presenter间不再有重复冗余的代码。但第二个问题:如果App项目在网络请求中夹杂了蓝牙等非常多的重度操作,该如何优化,这问题还是没解决。所以,如果你也被第二个问题所困扰,不妨期待接下来的第二个篇章。

相关文章

网友评论

      本文标题:Android开发—MVPO框架模式(一)

      本文链接:https://www.haomeiwen.com/subject/olxfzqtx.html