Android开发框架模式(MVC、MVP、MVVM)实例解析

Android项目中,尤其是比较大型的项目开发中,模块内部的高聚合和模块间的低耦合性就显得尤为重要了。所以我们一般情况下需要为项目设计一种框架模式,通常情况下我们一般用到的三种MVCMVPMVVM
通过框架模式设计的项目能够极大的提高开发效率,提高项目的可维护性和可扩展性,另外在模块测试及问题定位上也提供了较大的便利。

接下来我们就对这三种框架模式一一进行讲解,并会重点讲解下比较常用的MVP框架模式。

一、MVC框架:

                     图片源自网络

MVC全称为Model(模型层)--View(视图层)--Controller(控制层)。
    Model层:主要用于网络请求、数据库、业务逻辑处理等操作。
    View层:用于展示UI,一般采用XML文件进行界面的描述。
    Controller层:控制层的重任落在了众多Activity上,Activity需要交割业务逻辑至Model层处理。

下面结合精简的实例来看下MVC框架模式:

1、View层&Controller层

View层:XML布局文件activity_mvcpattern代表的就是View层,用来显示布局,与用户进行交互。

Controller层:MVCActivity代表的是Controller层,View层会传递请求至Controller,Controller控制Model层进行业务的更新。


/**
 * @author hongri
 * @date 2018/9/4
 *
 * Controller层
 */
public class MVCActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btnRequest;
    private ImageView iv;
    private MVCHttpRequestModel mvcHttpRequestModel;
    private static final String requestUrl
        = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1536307707406&di"
        + "=edafdb6ac08325cbabe1bf4fdb930cec&imgtype=0&src=http%3A%2F%2Fpic.qiantucdn"
        + ".com%2F58pic%2F18%2F23%2F07%2F54F58PIC2yq_1024.jpg";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        /**
         * xml文件属于理论上的View层
         */
        setContentView(R.layout.activity_mvcpattern);

        mvcHttpRequestModel = new MVCHttpRequestModel();

        btnRequest = findViewById(R.id.btnRequest);
        iv = findViewById(R.id.iv);
        btnRequest.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnRequest:

                /**
                 * Controller层将网络请求业务逻辑交由Model层处理
                 */
                mvcHttpRequestModel.onHttpRequest(requestUrl, new MVCRequestCallback() {

                    @Override
                    public void onSuccess(Object successData) {
                        if (successData != null) {
                            iv.setImageBitmap((Bitmap)successData);
                        }
                    }

                    @Override
                    public void onFailure(Object failureData) {
                        Toast.makeText(MVCActivity.this, failureData.toString(), Toast.LENGTH_LONG).show();
                    }
                });
                break;
            default:
                break;
        }
    }
}

2、Model层:

这里的MVCHttpRequestModel便属于Model层,Model层主要用于处理数据库、网络请求等一系列复杂的耗时处理,Model数据请求完成后,通知View进行UI的更新。



/**
 * @author hongri
 * @date 2018/9/4
 *
 * Model层
 */

public class MVCHttpRequestModel implements MVCHttpRequestInterface {

    @Override
    public void onHttpRequest(final String urlString, final MVCRequestCallback listener) {

        new RequestTask(listener).execute(urlString);

    }

    public class RequestTask extends AsyncTask<String, Void, Bitmap> {

        private MVCRequestCallback listener;

        public RequestTask(MVCRequestCallback listener) {
            this.listener = listener;
        }

        @Override
        protected Bitmap doInBackground(String... strings) {
            Bitmap bitmap = null;
            InputStream inputStream;
            try {
                URL url = new URL(strings[0]);
                HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(10 * 1000);
                int responseCode = conn.getResponseCode();

                if (responseCode == 200) {
                    inputStream = conn.getInputStream();
                    bitmap = BitmapFactory.decodeStream(inputStream);
                } else {
                    bitmap = null;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);

            /**
             *Model层请求完数据后回调给Controller层,Controller层更新UI
             */
            if (bitmap != null) {
                listener.onSuccess(bitmap);
            } else {
                listener.onFailure("获取失败");
            }
        }
    }
}

二、MVP框架

                  图片源自网络

MVP全称为Model(模型层)--View(视图层)--Presenter(协调器/主持者)

MVP是由MVC转化而来的一种框架模式,MVP相比于MVC具有的优点如下:

1、Model层与View层完全分离,我们可以修改视图而不影响模型;

2、可以更高效地使用模型,因为所有的交互都发生在一个地方---Presenter内部;

3、可以将大量的逻辑操作放到Presenter中,避免Activity的臃肿;

4、可以选择将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁。

下面根据代码来具体介绍下MVP框架:

1、首先介绍下View层:

View层一般由多个Activity来承担,主要用于显示UI,与用户来进行交互。



/**
 * MVP(Model--View--Presenter)框架模式--参考:http://www.jcodecraeer.com/a/anzhuokaifa/2017/1020/8625.html
 *
 * @author hongri
 *
 * View层
 */
public class MVPActivity extends MVPBaseActivity implements MVPViewInterface, OnClickListener {

    private Button btnName;
    private TextView tvData;
    private MVPPresenter mvpPresenter;
    private String requestUrl = "";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvppattern);

        mvpPresenter = new MVPPresenter();
        mvpPresenter.attachView(this);

        btnName = findViewById(R.id.btnName);
        tvData = findViewById(R.id.tvData);

        btnName.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnName:
                tvData.setText("初始数据");
                mvpPresenter.requestData(requestUrl);
                break;
            default:
                break;
        }
    }

    @Override
    public void showSuccessData(String data) {
        MVPDataInfo dataInfo = ParseJsonUtil.parseJSONData(data);
        tvData.setText(
            "姓名:" + dataInfo.getmName() + "\n" + "性别:" + dataInfo.getmGender() + "\n" + "年龄:" + dataInfo.getmAge());
    }

    @Override
    public void showFailureData(String data) {
        tvData.setText(data);
    }

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

这里为MVPActivity设置了一个顶级父类MVPBaseActivity,父类Activity中可以提炼出公用的UI展示、初始化等操作,减少各子Activity的冗余代码,也方便后续的维护。



/**
 * @author hongri
 * BaseActivity用于进行View层的通用的初始化及UI展示工作
 */
public class MVPBaseActivity extends AppCompatActivity implements MVPBaseViewInterface {

    /**
     * 页面通用LoadingDialog
     */
    private LoadingDialog loadingDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvpbase);
        loadingDialog = new LoadingDialog(this, R.style.LoadingTheme);
    }

    @Override
    public void showLoading() {
        if (loadingDialog != null) {
            loadingDialog.showLoading();
        }
    }

    @Override
    public void dismissLoading() {
        if (loadingDialog != null) {
            loadingDialog.dismissLoading();
        }
    }
}

接口也采用Base层继承的方式,分别定义了MVPViewInterface,及MVPBaseViewInterface,分别用于MVPActivity及MVPBaseActivity的回调,其中子接口根据业务区别一般是需要建立多个的。

/**
 * @author hongri
 * @date 2018/9/4
 *
 * 记录某个业务方所需的回调方法
 */

public interface MVPViewInterface extends MVPBaseViewInterface {

    /**
     * 业务方数据成功获取后,调用此方法更新UI
     *
     * @param data
     */
    void showSuccessData(String data);

    /**
     * 业务方数据获取失败后,调用此方法展示失败页面UI
     *
     * @param data
     */
    void showFailureData(String data);

}

/**
 * @author hongri
 * @date 2018/9/3
 *
 * 该接口记录所有页面通用的回调方法
 */

public interface MVPBaseViewInterface {

    /**
     * 展示通用页面Loading
     */
    void showLoading();

    /**
     * 取消通用页面Loading
     */
    void dismissLoading();
}

2、Presenter层:

  Presenter层主要用于业务逻辑操作,避免了Activity的代码臃肿。

/**
 * @author hongri
 * @date 2018/9/3
 *
 * Presenter层
 */

public class MVPPresenter extends MVPBasePresenter<MVPViewInterface> implements MVPLoadDataCallback {
    /**
     * 请求数据入口
     *
     * @param url
     */
    public void requestData(String url) {
        getAttachView().showLoading();
        MVPDataModelManager.newInstance(MVPDataModel.class.getName()).setParams("").executeGetRequest(url, this);
    }

    /**
     * 数据请求成功--更新UI
     *
     * @param successData
     */
    @Override
    public void onSuccess(String successData) {
        if (isViewAttached()) {
            getAttachView().dismissLoading();
            getAttachView().showSuccessData(successData);
        }
    }

    /**
     * 数据请求失败--更新展示错误页面UI
     *
     * @param errorData
     */
    @Override
    public void onFailure(String errorData) {
        if (isViewAttached()) {
            getAttachView().dismissLoading();
            getAttachView().showFailureData(errorData);
        }
    }
}
/**
 * @author hongri
 * @date 2018/9/4
 */

public class MVPBasePresenter<V extends MVPBaseViewInterface> {

    public V mView;

    /**
     * 绑定mView,一般在初始化中调用该方法
     */
    public void attachView(V view) {
        mView = view;
    }

    /**
     * 销毁mView,一般在onDestroy方法中调用
     */
    public void detachView() {
        if (mView != null) {
            mView = null;
        }
    }

    /**
     * 是否与mView建立联系
     * 每次调用业务方请求的时候都要先调用此方法检查是否与mView建立联系
     */
    public boolean isViewAttached() {
        return mView != null;
    }

    /**
     * 获取连接的mView
     */
    public V getAttachView() {
        return mView;
    }
}

3、Model层:

 该Model层与MVC模式中的Model层类似,项目中所有数据都由该类流入和流出,负责分发所有的请求数据。

/**
 * @author hongri
 * @date 2018/9/3
 *
 * Model层
 */

public class MVPDataModel extends MVPBaseModel {

    public String data = "";

    @Override
    public void executeGetRequest(String url, final MVPLoadDataCallback callback) {
        /**
         * 模拟网络请求耗时操作
         */
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                /**
                 * 从服务端请求回来的字符串数据:
                 * 为了增加通用性,建议以请求回来的原有数据返回给View层,由View层做具体的数据解析、UI展示处理
                 */
                data = "{\n"
                    + "  \"mData\":\"红日\",\n"
                    + "  \"gender\": \"男士\",\n"
                    + "  \"age\":18\n"
                    + "}";
                if (!TextUtils.isEmpty(data)) {
                    callback.onSuccess(data);
                } else {
                    data = "数据获取失败";
                    callback.onFailure(data);
                }
            }
        }, 1000);
    }
}

这里也封装了一个父类base层--MVPBaseModel,MVPBaseModel是所有Model的顶级父类,负责对外提供数据请求标准,对内为所有Model提供请求的底层支持。

/**
 * @author hongri
 * @date 2018/9/5
 */

public abstract class MVPBaseModel {

    protected String[] mParams;

    /**
     * 设置数据请求参数
     *
     * @param args 参数数组
     */
    public MVPBaseModel setParams(String... args) {
        mParams = args;
        return this;
    }

    /***
     * 执行Get网络请求
     * @param url
     * @param callback
     */
    public abstract void executeGetRequest(String url, MVPLoadDataCallback callback);

    /**
     * 执行Post网络请求
     *
     * @param url
     * @param params
     * @param callback
     */
    protected void executePostRequest(String url, Map params, MVPLoadDataCallback callback) {

    }
}

另外这里也定义了一个DataModelManager,是针对多有Model的管理类,把其作为入口,利用反射机制可以获得对应Model对象的引用。

/**
 * @author hongri
 * @date 2018/9/5
 *
 * 统一Model管理器
 */

public class MVPDataModelManager {

    public static MVPBaseModel mvpModel;

    public static MVPBaseModel newInstance(String modelName) {

        try {
            mvpModel = (MVPBaseModel)Class.forName(modelName).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return mvpModel;
    }
}

三、MVVM框架

          图片源自网络

MVVM的全称是Model(模型)--View(视图)--ViewModel(视图模型):

MVVM可以算是MVP的升级版,将 Presenter 改名为 ViewModel。关键在于View和Model的双向绑定,当View有用户输入后,ViewModel通知Model更新数据,同理Model数据更新后,ViewModel通知View更新。

MVVM的优势如下:

1、ViewModel双向绑定,一方的改变都会影响另一方,开发者不用再去手动修改UI的数据。

2、不需要findViewById也不需要butterknife,不需要拿到具体的View去设置数据绑定监听器等等,这些都可以用DataBinding完成。

3、ViewModel的双向绑定是支持生命周期检测的,不会担心页面销毁了还有回调发生,这个由lifeCycle完成。

4、不会像MVC一样导致Activity中代码量巨大,也不会像MVP一样出现大量的ViewPresenter接口。项目结构更加低耦合。

下面具体介绍下MVVM框架:

1、VM层:

这里根据MVVMActivity的布局文件activity_mvvmpattern,介绍下Data Binding:

Data Binding对使用的环境还是有一定要求的:Android Studio版本在1.3以上,gradle的版本要在1.5.0-alpha1以上。
需要在Android SDK manager中下载Android Support repository
然后在对应的Module的build.gradle中添加:

android {
  ....
  dataBinding {
      enabled =true
  }
}

然后使用databinding语法 对 xml 进行数据绑定,我们将 Click事件、输出结果都绑定到VM上。

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

    <data>

        <import type="android.view.View"/>

        <variable
            name="userViewModel"
            type="com.hongri.model.mvvm.viewmodel.MVVMDataViewModel"/>

        <variable
            name="handlers"
            type="com.hongri.model.mvvm.view.MVVMActivity"/>

        <variable
            name="data"
            type="String"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <Button
            android:id="@+id/btnMVVM"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{handlers.onClickLoadData}"
            android:text="点击请求数据"/>

        <Button
            android:id="@+id/btnToast"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{handlers.onClickShowToastName}"
            android:text="点击请求数据并Toast提示"/>

        <TextView
            android:id="@+id/tvData"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@{userViewModel.data}"/>
    </LinearLayout>
</layout>

VM层具体类:这里注意具体的属性需要加@Bindable,否则绑定无效。

/**
 * @author zhongyao
 * @date 2018/9/3
 * ViewModel层
 */

public class MVVMDataViewModel extends BaseObservable implements MVVMLoadDataCallback {
    private MVVMDataModel model;

    public MVVMDataViewModel() {
        model = new MVVMDataModel();
    }

    /**
     * 必须添加@Bindable注释
     * @return
     */
    @Bindable
    public String getData() {
        return model.mData;
    }

    public void loadUserData() {
        model.requestData(this);
    }

    @Override
    public void onSuccess() {
        notifyPropertyChanged(BR.data);
    }

    @Override
    public void onFailure() {

    }
}

2、View层(MVVMActivity)引入VM:

/**
 * @author hongri
 * View层
 */
public class MVVMActivity extends AppCompatActivity {

    private MVVMDataViewModel userViewModel;
    private TextView tvData;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMvvmpatternBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvmpattern);
        userViewModel = new MVVMDataViewModel();
        binding.setUserViewModel(userViewModel);
        binding.setHandlers(this);

        tvData = binding.tvData;

    }

    public void onClickShowToastName(View view) {
        Toast.makeText(this, tvData.getText().toString(), Toast.LENGTH_LONG).show();
    }

    public void onClickLoadData(View view) {
        userViewModel.loadUserData();
    }
}

3、Model层:

 用来进行具体的数据请求操作。

/**
 * @author hongri
 * @date 2018/9/3
 * Model层
 */

public class MVVMDataModel {
    public String mData;

    public MVVMDataModel() {
        this.mData = "初始数据";
    }

    public void requestData(MVVMLoadDataCallback callback) {
        this.mData = "数据请求成功";
        callback.onSuccess();
    }
}

MVVM虽然有这些个优点,但使用起来的坑也是不少的,所以目前MVVM框架在实际开发中应用并不是很多,主要以MVP框架开发为主。

最后提供下github源码,供大家参考:

源码下载

参考:

https://github.com/googlesamples/android-architecture

发布了95 篇原创文章 · 获赞 195 · 访问量 26万+

猜你喜欢

转载自blog.csdn.net/u012440207/article/details/82493518