Android中MVP设计框架浅析

一、MVP设计模式简介

目前Android设计成熟的框架有MVC,MVP和MVVM,MVP是由MCV演变而来,MVVM是MVP的进一步升级。三种框架模式并没有完全的哪一种最好,只有在项目的需求上哪一种框架最适合。根据前人总结的项目经验,小项目用MVC,中型项目用MVP,大型项目用MVVM。

二、MVP版本的演进史

2.1 MVC在Android开发中的局限

MVP模式是由MVC发展演变而来的,MVC模式的UML图存在如下两个版本。其中版本一参见于MVP的百度百科和标准的MVC网络教程;版本二可以见于部分Android MVC和MVP的介绍网站。


Android中MVP设计框架浅析_第1张图片

                                                                     MVC 版本一


Android中MVP设计框架浅析_第2张图片

                                                              MVC 版本二

标准MVC在软件开发中的流程设定是,view层负责界面显示逻辑,Control层负责业务流程逻辑,Model层负责数据的存取逻辑。view层持有Control层的引用,Control层持有Model层的引用,Control层持有View层的引用,当View层有用户操作需要获取数据时,会通过Control层控制Model层进行数据的获取,最后在通过Model层回显到View层。MVC实现了在界面、业务逻辑和数据的分层设计,在改进这三层设计中的某一层设计时,不需要重新编写整个代码。

在Android的设计中,通常是用一个Activity组件设计一个页面的交互逻辑。在Activity的页面设计中,并不容易将View和Controller进行分层处理,这就导致View层会持有Model层的引用,就形成了MVC版本二的UML图。参见图下示例代码,


public class MainActivity extends ActionBarActivity implements OnWeatherListener, View.OnClickListener {

private WeatherModel weatherModel;

private EditText cityNOInput;

private TextView city;

...

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

weatherModel = new WeatherModelImpl();

initView();

}

//初始化View

private void initView() {

cityNOInput = findView(R.id.et_city_no);

city = findView(R.id.tv_city);

...

findView(R.id.btn_go).setOnClickListener(this);

}

//显示结果

public void displayResult(Weather weather) {

WeatherInfo weatherInfo = weather.getWeatherinfo();

city.setText(weatherInfo.getCity());

...

}

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.btn_go:

weatherModel.getWeather(cityNOInput.getText().toString().trim(), this);

break;

}

}

@Override

public void onSuccess(Weather weather) {

displayResult(weather);

}

@Override

public void onError() {

Toast.makeText(this, 获取天气信息失败, Toast.LENGTH_SHORT).show();

}

private T findView(int id) {

return (T) findViewById(id);

}

}



在代码中onClick(View v)内的代码就负责了业务层的部分逻辑,并且很明显可以看到Model层的引用weatherModel。

上述代码中只要有一个按钮点击的事件,当Activity页面存在多个事件操作时,Activity的代码必定会显得非常臃肿,这导致的结果就是破坏了MVC原本的分层逻辑设计,任何一处代码的修改都会带来较大的代码改动。

2.2 Android中MVP设计模式的引入

鉴于MVC在Android设计上出现的缺陷,因此在设计框架上引入了MVP,MVP的UML图下


从UML图可以看出,在MVP设计模式中,Presenter层完全隔离了View层和Model层,主要的程序逻辑在Presenter里进行实现。

以Google提供的MVP代码([https://github.com/googlesamples/android-architecture/tree/todo-mvp/](https://github.com/googlesamples/android-architecture/tree/todo-mvp/))来进行分析。

View层的代码如下



public class TaskDetailFragment extends Fragment implements TaskDetailContract.View {

@NonNull

private static final String ARGUMENT_TASK_ID = "TASK_ID";

@NonNull

private static final int REQUEST_EDIT_TASK = 1;

private TaskDetailContract.Presenter mPresenter;

private TextView mDetailTitle;

private TextView mDetailDescription;

private CheckBox mDetailCompleteStatus;

public static TaskDetailFragment newInstance(@Nullable String taskId) {

Bundle arguments = new Bundle();

arguments.putString(ARGUMENT_TASK_ID, taskId);

TaskDetailFragment fragment = new TaskDetailFragment();

fragment.setArguments(arguments);

return fragment;

}

@Override

public void onResume() {

super.onResume();

mPresenter.start();

}

@Nullable

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

View root = inflater.inflate(R.layout.taskdetail_frag, container, false);

setHasOptionsMenu(true);

mDetailTitle = (TextView) root.findViewById(R.id.task_detail_title);

mDetailDescription = (TextView) root.findViewById(R.id.task_detail_description);

mDetailCompleteStatus = (CheckBox) root.findViewById(R.id.task_detail_complete);

// Set up floating action button

FloatingActionButton fab =

(FloatingActionButton) getActivity().findViewById(R.id.fab_edit_task);

fab.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

mPresenter.editTask();

}

});

return root;

}

@Override

public void setPresenter(@NonNull TaskDetailContract.Presenter presenter) {

mPresenter = checkNotNull(presenter);

}

@Override

public boolean onOptionsItemSelected(MenuItem item) {

switch (item.getItemId()) {

case R.id.menu_delete:

mPresenter.deleteTask();

return true;

}

return false;

}

...

@Override

public void showCompletionStatus(final boolean complete) {

Preconditions.checkNotNull(mDetailCompleteStatus);

mDetailCompleteStatus.setChecked(complete);

mDetailCompleteStatus.setOnCheckedChangeListener(

new CompoundButton.OnCheckedChangeListener() {

@Override

public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

if (isChecked) {

mPresenter.completeTask();

} else {

mPresenter.activateTask();

}

}

});

}

...

}



Presenter层的代码设计



package com.example.android.architecture.blueprints.todoapp.taskdetail;

import android.support.annotation.NonNull;

import android.support.annotation.Nullable;

import com.example.android.architecture.blueprints.todoapp.data.Task;

import com.example.android.architecture.blueprints.todoapp.data.source.TasksDataSource;

import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepository;

import com.google.common.base.Strings;

import static com.google.common.base.Preconditions.checkNotNull;

/**

* Listens to user actions from the UI ({@link TaskDetailFragment}), retrieves the data and updates

* the UI as required.

*/

public class TaskDetailPresenter implements TaskDetailContract.Presenter {

private final TasksRepository mTasksRepository;

private final TaskDetailContract.View mTaskDetailView;

@Nullable

private String mTaskId;

public TaskDetailPresenter(@Nullable String taskId,

@NonNull TasksRepository tasksRepository,

@NonNull TaskDetailContract.View taskDetailView) {

mTaskId = taskId;

mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");

mTaskDetailView = checkNotNull(taskDetailView, "taskDetailView cannot be null!");

mTaskDetailView.setPresenter(this);

}

@Override

public void start() {

openTask();

}

private void openTask() {

if (Strings.isNullOrEmpty(mTaskId)) {

mTaskDetailView.showMissingTask();

return;

}

mTaskDetailView.setLoadingIndicator(true);

mTasksRepository.getTask(mTaskId, new TasksDataSource.GetTaskCallback() {

@Override

public void onTaskLoaded(Task task) {

// The view may not be able to handle UI updates anymore

if (!mTaskDetailView.isActive()) {

return;

}

mTaskDetailView.setLoadingIndicator(false);

if (null == task) {

mTaskDetailView.showMissingTask();

} else {

showTask(task);

}

}

@Override

public void onDataNotAvailable() {

// The view may not be able to handle UI updates anymore

if (!mTaskDetailView.isActive()) {

return;

}

mTaskDetailView.showMissingTask();

}

});

}

@Override

public void editTask() {

if (Strings.isNullOrEmpty(mTaskId)) {

mTaskDetailView.showMissingTask();

return;

}

mTaskDetailView.showEditTask(mTaskId);

}

@Override

public void deleteTask() {

if (Strings.isNullOrEmpty(mTaskId)) {

mTaskDetailView.showMissingTask();

return;

}

mTasksRepository.deleteTask(mTaskId);

mTaskDetailView.showTaskDeleted();

}

@Override

public void completeTask() {

if (Strings.isNullOrEmpty(mTaskId)) {

mTaskDetailView.showMissingTask();

return;

}

mTasksRepository.completeTask(mTaskId);

mTaskDetailView.showTaskMarkedComplete();

}

@Override

public void activateTask() {

if (Strings.isNullOrEmpty(mTaskId)) {

mTaskDetailView.showMissingTask();

return;

}

mTasksRepository.activateTask(mTaskId);

mTaskDetailView.showTaskMarkedActive();

}

private void showTask(@NonNull Task task) {

String title = task.getTitle();

String description = task.getDescription();

if (Strings.isNullOrEmpty(title)) {

mTaskDetailView.hideTitle();

} else {

mTaskDetailView.showTitle(title);

}

if (Strings.isNullOrEmpty(description)) {

mTaskDetailView.hideDescription();

} else {

mTaskDetailView.showDescription(description);

}

mTaskDetailView.showCompletionStatus(task.isCompleted());

}

}



从代码的设计上可以看出View层逻辑上相关的代码都交由Presenter的引用在进行处理,在Presenter层中持有View层和Model层的引用,Presenter层可以回调更新UI界面。在Google提供代码的Data目录下,并没有看到持有Presenter层的引用,这一点并不违反MVP的设计框架,MVP的设计框架的思想是为了对view、model和presenter进行分层,能够在代码设计中实现这一点已经足够了。另外在代码设计中为了秉承接口隔离的设计原则,在MVP的代码中采用很多接口进行耦合。

三、MVP设计的总结

MVP是在MVC上的改进,这并非代表着在Android设计中MVP设计模式一定比MVC好,比如在某些项目中,数据从Model层经过Presenter层拷贝到View层的开销比较大,这种情况下可能需要使用MVC的更为合适,通过定义一个接口也可以起到一定的隔离和分层左右。在代码设计中需要做的是尽可能的秉承设计的六项基本原则,根据项目的大小合理的选择框架就够了。

你可能感兴趣的:(Android中MVP设计框架浅析)