Android开发框架模式(MVC、MVP、MVVM)实例解析
Android项目中,尤其是比较大型的项目开发中,模块内部的高聚合和模块间的低耦合性就显得尤为重要了。所以我们一般情况下需要为项目设计一种框架模式,通常情况下我们一般用到的三种MVC、MVP、MVVM。
通过框架模式设计的项目能够极大的提高开发效率,提高项目的可维护性和可扩展性,另外在模块测试及问题定位上也提供了较大的便利。
接下来我们就对这三种框架模式一一进行讲解,并会重点讲解下比较常用的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、View
和Model
双向绑定,一方的改变都会影响另一方,开发者不用再去手动修改UI的数据。
2、不需要findViewById
也不需要butterknife
,不需要拿到具体的View
去设置数据绑定监听器等等,这些都可以用DataBinding
完成。
3、View
和Model
的双向绑定是支持生命周期检测的,不会担心页面销毁了还有回调发生,这个由lifeCycle
完成。
4、不会像MVC
一样导致Activity
中代码量巨大,也不会像MVP
一样出现大量的View
和Presenter
接口。项目结构更加低耦合。
下面具体介绍下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源码,供大家参考: