浅谈MVC,MVP,MVVM三种架构模式

1.MVC架构模式

View:对应的xml布局文件
Model:实体模型(JavaBean)
Controllor:对应于Activity业务逻辑,数据处理和UI处理

该模式的特点,就是当某个界面非常复杂的时候,activity代码量会上千行的增加,非常冗余。

2.MVP架构模式

View:对应的xml布局文件
Model:实体模型(JavaBean)
Presenter:责完成View于Model间的交互和业务逻辑

搭建MVP的一个栗子

(1).创建一个持有View接口的基类的BasePersenter

public class BasePersenter<T> implements Persenter<T> {
    protected T mView;//View接口
    protected WebService mRetrofit = MyRertofit.getLoginService().create(WebService.class);
    protected CompositeSubscription mCompositeSubscription;

    public BasePersenter(T t) {
        attachView(t);
    }

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

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

    public void onUnsubscribe() {
        if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) {
            mCompositeSubscription.unsubscribe();
        }
    }

    /***
     * 通用的网络请求的方法
     * @param observable
     * @param subscriber
     */
    public void addSubscription(Observable observable, Subscriber subscriber) {
        if (mCompositeSubscription == null) {
            mCompositeSubscription = new CompositeSubscription();
        }
        if (!NetworkUtils.isConnected()) {
            ToastUtils.showShort("网络不可用,请检查网络~");
        }
        mCompositeSubscription.add(observable
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(subscriber));
    }
}

(2).新建一个继承BasePersenter的PayCarPersenter

public class PayCarPersenter extends BasePersenter<PayCarView>{
    public PayCarPersenter(PayCarView baseResultBaseView) {
        super(baseResultBaseView);
    }

    public void  getDetail(String rechargeAmount,String giftAmount){
        mView.OnGetPayInfoLoading();
        HashMap<String, String> map = new HashMap();
        map.put("userId",  SharedPreferencesUtils.getString(App.application, Constans.USER_ID, ""));
        map.put("rechargeAmount",rechargeAmount);
        map.put("giftAmount",giftAmount);
        map.put("terminal","1");
        addSubscription(mRetrofit.recharge(map),new SubscriberCallBack<>(new ApiCallback<BaseResult<PayCarBean>>() {
            @Override
            public void onSuccess(BaseResult<PayCarBean> payCarBeanBaseResult) {
                if (payCarBeanBaseResult.getSuccess()){
                    mView.OnGetPayInfoSuccess(payCarBeanBaseResult.getResultObject());
                }else{
                    mView.OnGetPayInfoError("获取充值信息失败,请重试~");
                }
            }

            @Override
            public void onFailure(int code, String msg) {
                mView.OnGetPayInfoError("获取充值信息失败,请重试~");
            }

            @Override
            public void onCompleted() {
                mView.OnGetPayInfoComplete();
            }
        }));
    }

    public void toPay(String orderId){
        mView.OnToPayLoading();
        HashMap<String, String> map = new HashMap();
        map.put("orderNumber",  orderId);
        map.put("payType","11");
        map.put("ip", NetworkUtils.getIPAddress(true));
        map.put("orderType","recharge");
        map.put("payWay","4");
        map.put("isRaw","1");
        addSubscription(mRetrofit.getzxPlay(map),new SubscriberCallBack<>(new ApiCallback<BaseResult<Object>>() {
            @Override
            public void onSuccess(BaseResult<Object> stringBaseResult) {
                if (stringBaseResult.getSuccess()){
                    mView.OnToPaySuccess(stringBaseResult.getResultObject());
                }else{
                    mView.OnToPayError("获取支付信息失败,请重试~");
                }
            }

            @Override
            public void onFailure(int code, String msg) {
                mView.OnToPayError("获取支付信息失败,请重试~");
            }

            @Override
            public void onCompleted() {
                mView.OnToPayComplete();
            }
        }));
    }
}

(3).PayCarView 接口

public interface PayCarView {
void OnGetPayInfoSuccess(PayCarBean bean);

void OnGetPayInfoError(String msg);

void OnGetPayInfoComplete();

void OnGetPayInfoLoading();

void OnToPaySuccess(Object bean);

void OnToPayError(String msg);

void OnToPayComplete();

void OnToPayLoading();
}

(4).新建一个持有presenter基类MvpActivity或者MvpFragment

public abstract class MvpActivity<P extends BasePersenter> extends BaseActivity {
    protected P mPersenter;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        mPersenter = createPersenter();
        super.onCreate(savedInstanceState);

    }

    protected abstract P createPersenter();

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPersenter != null)
            mPersenter.detachView();
    }
}

(5).新建一个继承自MvpActivity的子Activity,实现抽象方法createPersenter()

public class PayCarActivity extends MvpActivity<PayCarPersenter> implements PayCarView {
    @BindView(R.id.tv_zhensong)
    TextView tv_zhensong;
    @BindView(R.id.tv_shij)
    TextView tv_shij;
    @BindView(R.id.tv_money)
    TextView tv_money;
    @BindView(R.id.tv_order_id)
    TextView tv_order_id;
    private IWXAPI api;

    @Override
    protected PayCarPersenter createPersenter() {
        return new PayCarPersenter(this);
    }

    @Override
    protected void onError() {
        initData();
    }

    @Override
    protected void onBack() {
        showGiveUpPayDialog();
    }

    @Override
    protected int setRootView() {
        return R.layout.activity_pay_cart;
    }

    @Override
    protected void initData() {
        mPersenter.getDetail(getStringExtra("money"), getStringExtra("disMoney"));
    }

    /***
     * 放弃支付提示框
     */
    private void showGiveUpPayDialog() {
        DialogUtils.showDialog(this, "提示", "确定放弃充值?使用会员卡储值,\n立享更多优惠哦?", maAlertDialog -> {
            maAlertDialog.dismiss();
            finish();

        });
    }

    @Override
    protected void initToolBar() {
        setTitle("确认支付");
    }

    @Override
    protected void initView(Bundle savedInstanceState) {
        api = WXAPIFactory.createWXAPI(this, Constans.APP_ID);
    }

    @OnClick(R.id.ll_wx_pay)
    public void toPay() {
        if (api.isWXAppInstalled()) {
            //检测当前微信版本是否支持微信支付
            boolean isPaySupported = api.getWXAppSupportAPI() >= Build.PAY_SUPPORTED_SDK_INT;
            if (isPaySupported) {
                mPersenter.toPay(tv_order_id.getText().toString());
            } else {
                ToastUtils.showLong("您当前的微信版本过低,不支持微信支付");
            }
        } else {
            ToastUtils.showLong("您没有安装微信,请您安装后再支付");
        }

    }

    @Override
    public void OnGetPayInfoSuccess(PayCarBean bean) {
        tv_money.setText(bean.getRechargeAmount() + "");
        tv_order_id.setText(bean.getOrderId());
        tv_shij.setText(bean.getRealAmount() + "元");
        tv_zhensong.setText(bean.getGiftAmount() + "元");
        overLoading();
    }

    @Override
    public void OnGetPayInfoError(String msg) {
        showToast(msg);
        showError();
    }

    @Override
    public void OnGetPayInfoComplete() {

    }

    @Override
    public void OnGetPayInfoLoading() {
        toLoading();
    }

    @Override
    public void OnToPaySuccess(Object bean) {
        App.rechargeMoney = tv_money.getText().toString().trim();
        JSONObject jsonObject = null;
        JSONObject json = null;
        try {
            jsonObject = new JSONObject(bean.toString());
            json = jsonObject.optJSONObject("pay_info");
            PayReq req = new PayReq();
            req.appId = json.optString("appid");
            req.partnerId = json.optString("partnerid");
            req.prepayId = json.optString("prepayid");
            req.nonceStr = json.optString("noncestr");
            req.timeStamp = json.optString("timestamp");
            req.packageValue = json.optString("package");
            req.sign = json.optString("sign");
            req.extData = "app data";
            Constans.payFrom = 3;
            api.sendReq(req);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void OnToPayError(String msg) {
        showToast(msg);
    }

    @Override
    public void OnToPayComplete() {
        closeLoading();
    }

    @Override
    public void OnToPayLoading() {
        showLoadding("正在获取支付信息~");
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        // TODO Auto-generated method stub
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            showGiveUpPayDialog();
        }
        return false;
    }

    @Subscribe
    public void toWebWxPay(WebPayEvent webPayEvent) {
        switch (webPayEvent.getVar()) {
            case 0:
                startActivity(RechargeSuccessActivity.class);
                AppManager.getAppManager().finishActivity(StoreValueCardActivity.class);
                finish();
                break;
            default:
                DialogUtils.showNoCancleDialog(PayCarActivity.this, "支付说明", "用户支付失败或取消支付", maAlertDialog -> {
                    maAlertDialog.dismiss();
                });
                break;
        }

    }
}
核心就是Activity实现View的接口,并持有Presenter对象,Presenter中持有Model和View对象,负责完成View于Model间的交互和业务逻辑,本文中的Prsenter并没有Model,而是直接新建了一些类似model中的请求方法,这样可以省掉Model接口和ModelIml接口实现类,简化代码。

3.MVVM架构

View:对应的xml布局文件
Model:实体模型(JavaBean)
ViewModel:责完成View于Model间的交互和业务逻辑(依赖于Google的Databing框架)

MVVM的目标和思想MVP类似,利用数据绑定(Data Binding)、依赖属性(Dependency Property)等新特性,打造了一个更加灵活高效的架构。

(1)######DataBing框架的使用(https://www.jianshu.com/p/d429174b8d07
ObservableField 以及它的派生 ObservableBoolean、 ObservableByte ObservableChar、ObservableShort、ObservableInt、ObservableLong、ObservableFloat、ObservableDouble、 ObservableParcelable 支持双向绑定,只要数据变了,xml文件中的数据会自动刷新。

image.png

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="doubleBindBean"
            type="com.zx.databindingdemo.bean.DoubleBindBean" />

        <variable
            name="doubleBindBean2"
            type="com.zx.databindingdemo.bean.DoubleBindBean2" />

        <variable
            name="list"
            type="android.databinding.ObservableArrayList&lt;String&gt;" />

        <variable
            name="map"
            type="android.databinding.ObservableArrayMap&lt;String,Object&gt;" />

        <variable
            name="onClickListener"
            type="android.view.View.OnClickListener" />

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="15dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{doubleBindBean.content}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{doubleBindBean2.username}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{list[0]}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text='@{map["key0"]}' />

        <Button
            android:id="@+id/change_content_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{onClickListener}"
            android:textAllCaps="false"
            android:text="BaseObservable方式改变内容" />

        <Button
            android:id="@+id/change_content_btn2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{onClickListener}"
            android:textAllCaps="false"
            android:text="ObservableFields方式改变内容" />


        <Button
            android:id="@+id/change_content_btn3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{onClickListener}"
            android:textAllCaps="false"
            android:text="list改变内容" />


        <Button
            android:id="@+id/change_content_btn4"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{onClickListener}"
            android:textAllCaps="false"
            android:text="map改变内容" />

    </LinearLayout>
</layout>

public class DoubleBindBean2 {
    //变量需要为public
    public final ObservableField<String> username = new ObservableField<>();
}
public class DoubleBindActivity extends AppCompatActivity implements View.OnClickListener {

    private DoubleBindBean2 doubleBindBean2;
    private boolean flag2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityDoubleBindBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_double_bind);

        doubleBindBean2 = new DoubleBindBean2();
        doubleBindBean2.username.set("我是原始内容2");
        binding.setDoubleBindBean2(doubleBindBean2);

        binding.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.change_content_btn2:
                //ObservableFields
                flag2 = !flag2;
                if (flag2) {
                    doubleBindBean2.username.set("我是更新后的内容2");
                } else {
                    doubleBindBean2.username.set("我是原始内容2");
                }
                break;
        }
    }
}

(2).基于DataBinding框架,搭建MVVM框架,和MVP类似
A:新建一个ViewModel的类似Presenter的类,持有bing对象,或着adapter对象,然后ViewModel中做网络请求,和然后通过bind.setBean()进行数据刷新即可,不需要回调到Activity中进行处理,直接在ViewModel中就完成了数据的更新。
B:Activity中持有ViewModel的对象,通过该对象,调用内部网络请求等各种方法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容