美文网首页
Android中MVP设计框架浅析

Android中MVP设计框架浅析

作者: polo198763 | 来源:发表于2018-03-08 15:33 被阅读0次

    一、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的介绍网站。

                                                                         MVC 版本一

                                                                  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设计框架浅析

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