Android MVP开发模式 google 官方Mvp架构详解(转)

article/2025/9/23 1:27:17

Google官方MVP Sample代码解读

关于Android程序的构架, 当前最流行的模式即为MVP模式, Google官方提供了Sample代码来展示这种模式的用法.
Repo地址: android-architecture.
本文为阅读官方sample代码的阅读笔记和分析.

官方Android Architecture Blueprints [beta]:
Android在如何组织和构架一个app方面提供了很大的灵活性, 但是同时这种自由也可能会导致app在测试, 维护, 扩展方面变得困难.

Android Architecture Blueprints展示了可能的解决方案. 在这个项目里, 我们用各种不同的构架概念和工具实现了同一个应用(To Do App). 主要的关注点在于代码结构, 构架, 测试和维护性.
但是请记住, 用这些模式构架app的方式有很多种, 要根据你的需要, 不要把这些当做绝对的典范.

MVP模式 概念

之前有一个MVC模式: Model-View-Controller.
MVC模式 有两个主要的缺点: 首先, View持有Controller和Model的引用; 第二, 它没有把对UI逻辑的操作限制在单一的类里, 这个职能被Controller和View或者Model共享.
所以后来提出了MVP模式来克服这些缺点.

MVP(Model-View-Presenter)模式:

  • Model: 数据层. 负责与网络层和数据库层的逻辑交互.
  • View: UI层. 显示数据, 并向Presenter报告用户行为.
  • Presenter: 从Model拿数据, 应用到UI层, 管理UI的状态, 决定要显示什么, 响应用户的行为.
    MVP模式的最主要优势就是耦合降低, Presenter变为纯Java的代码逻辑, 不再与Android Framework中的类如Activity, Fragment等关联, 便于写单元测试.

todo-mvp 基本的Model-View-Presenter架构

app中有四个功能:

  • Tasks
  • TaskDetail
  • AddEditTask
  • Statistics

每个功能都有:

  • 一个定义View和Presenter接口的Contract接口;
  • 一个Activity用来管理fragment和presenter的创建;
  • 一个实现了View接口的Fragment;
  • 一个实现了Presenter接口的presenter.

mvp

基类

Presenter基类:

public interface BasePresenter {void start();
}

例子中这个start()方法都在Fragment的onResume()中调用.

View基类:

public interface BaseView<T> {void setPresenter(T presenter);
}

View实现

  • Fragment作为每一个View接口的实现, 主要负责数据显示和在用户交互时调用Presenter, 但是例子代码中也是有一些直接操作的部分, 比如点击开启另一个Activity, 点击弹出菜单(菜单项的点击仍然是调用presenter的方法).
  • View接口中定义的方法多为showXXX()方法.

  • Fragment作为View实现, 接口中定义了方法:

    @Override
    public boolean isActive() {return isAdded();
    }

在Presenter中数据回调的方法中, 先检查View.isActive()是否为true, 来保证对Fragment的操作安全.

Presenter实现

  • Presenter的start()方法在onResume()的时候调用, 这时候取初始数据; 其他方法均对应于用户在UI上的交互操作.
  • New Presenter的操作是在每一个Activity的onCreate()里做的: 先添加了Fragment(View), 然后把它作为参数传给了Presenter. 这里并没有存Presenter的引用.
  • Presenter的构造函数有两个参数, 一个是Model(Model类一般叫XXXRepository), 一个是View. 构造中先用guava的checkNotNull()
    检查两个参数是否为null, 然后赋值到字段; 之后再调用View的setPresenter()方法把Presenter传回View中引用.

Model实现细节

  • Model只有一个类, 即TasksRepository. 它还是一个单例. 因为在这个应用的例子中, 我们操作的数据就这一份.

它由手动实现的注入类Injection类提供:

public class Injection {public static TasksRepository provideTasksRepository(@NonNull Context context) {checkNotNull(context);return TasksRepository.getInstance(FakeTasksRemoteDataSource.getInstance(),TasksLocalDataSource.getInstance(context));}
}

构造如下:

private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource,@NonNull TasksDataSource tasksLocalDataSource) {mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource);mTasksLocalDataSource = checkNotNull(tasksLocalDataSource);
}
  • 数据分为local和remote两大部分. local部分负责数据库的操作, remote部分负责网络. Model类中还有一个内存缓存.
  • TasksDataSource是一个接口. 接口中定义了Presenter查询数据的回调接口, 还有一些增删改查的方法.

单元测试

MVP模式的主要优势就是便于为业务逻辑加上单元测试.
本例子中的单元测试是给TasksRepository和四个feature的Presenter加的.
Presenter的单元测试, Mock了View和Model, 测试调用逻辑, 如:

public class AddEditTaskPresenterTest {@Mockprivate TasksRepository mTasksRepository;@Mockprivate AddEditTaskContract.View mAddEditTaskView;private AddEditTaskPresenter mAddEditTaskPresenter;@Beforepublic void setupMocksAndView() {MockitoAnnotations.initMocks(this);when(mAddEditTaskView.isActive()).thenReturn(true);}@Testpublic void saveNewTaskToRepository_showsSuccessMessageUi() {mAddEditTaskPresenter = new AddEditTaskPresenter("1", mTasksRepository, mAddEditTaskView);mAddEditTaskPresenter.saveTask("New Task Title", "Some Task Description");verify(mTasksRepository).saveTask(any(Task.class)); // saved to the modelverify(mAddEditTaskView).showTasksList(); // shown in the UI}...
}

todo-mvp-loaders 用Loader取数据的MVP

基于上一个例子todo-mvp, 只不过这里改为用Loader来从Repository得到数据.


todo-mvp-loaders

使用Loader的优势:

  • 去掉了回调, 自动实现数据的异步加载;
  • 当内容改变时回调出新数据;
  • 当应用因为configuration变化而重建loader时, 自动重连到上一个loader.

Diff with todo-mvp

既然是基于todo-mvp, 那么之前说过的那些就不再重复, 我们来看一下都有什么改动:
git difftool -d todo-mvp

添加了两个类:
TaskLoaderTasksLoader.

在Activity中new Loader类, 然后传入Presenter的构造方法.

Contract中View接口删掉了isActive()方法, Presenter删掉了populateTask()方法.

数据获取

添加的两个新类是TaskLoaderTasksLoader, 都继承于AsyncTaskLoader, 只不过数据的类型一个是单数, 一个是复数.

AsyncTaskLoader是基于ModernAsyncTask, 类似于AsyncTask,
把load数据的操作放在loadInBackground()里即可, deliverResult()方法会将结果返回到主线程, 我们在listener的onLoadFinished()里面就可以接到返回的数据了, (在这个例子中是几个Presenter实现了这个接口).

TasksDataSource接口的这两个方法:

List<Task> getTasks();
Task getTask(@NonNull String taskId);

都变成了同步方法, 因为它们是在loadInBackground()方法里被调用.

Presenter中保存了LoaderLoaderManager, 在start()方法里initLoader, 然后onCreateLoader返回构造传入的那个loader.
onLoadFinished()里面调用View的方法. 此时Presenter实现LoaderManager.LoaderCallbacks.

数据改变监听

TasksRepository类中定义了observer的接口, 保存了一个listener的list:

private List<TasksRepositoryObserver> mObservers = new ArrayList<TasksRepositoryObserver>();public interface TasksRepositoryObserver {void onTasksChanged();
}

每次有数据改动需要刷新UI时就调用:

private void notifyContentObserver() {for (TasksRepositoryObserver observer : mObservers) {observer.onTasksChanged();}
}

在两个Loader里注册和注销自己为TasksRepository的listener: 在onStartLoading()里add, onReset()里面remove方法.
这样每次TasksRepository有数据变化, 作为listener的两个Loader都会收到通知, 然后force load:

@Override
public void onTasksChanged() {if (isStarted()) {forceLoad();}
}

这样onLoadFinished()方法就会被调用.

todo-databinding

基于todo-mvp, 使用Data Binding library来显示数据, 把UI和动作绑定起来.

说到ViewModel, 还有一种模式叫MVVM(Model-View-ViewModel)模式.
这个例子并没有严格地遵循Model-View-ViewModel模式或者Model-View-Presenter模式, 因为它既用了ViewModel又用了Presenter.


mvp-databinding

Data Binding Library让UI元素和数据模型绑定:

  • layout文件用来绑定数据和UI元素;
  • 事件和action handler绑定;
  • 数据变为可观察的, 需要的时候可以自动更新.

Diff with todo-mvp

添加了几个类:

  • StatisticsViewModel;
  • SwipeRefreshLayoutDataBinding;
  • TasksItemActionHandler;
  • TasksViewModel;

从几个View的接口可以看出方法数减少了, 原来需要多个showXXX()方法, 现在只需要一两个方法就可以了.

数据绑定

TasksDetailFragment为例:
以前在todo-mvp里需要这样:

public void onCreateView(...) {...mDetailDescription = (TextView)
root.findViewById(R.id.task_detail_description);
}@Override
public void showDescription(String description) {mDetailDescription.setVisibility(View.VISIBLE);mDetailDescription.setText(description);
}

现在只需要这样:

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {View view = inflater.inflate(R.layout.taskdetail_frag, container, false);mViewDataBinding = TaskdetailFragBinding.bind(view);...
}@Override
public void showTask(Task task) {mViewDataBinding.setTask(task);
}

因为所有数据绑定的操作都写在了xml里:

<TextViewandroid:id="@+id/task_detail_description"...android:text="@{task.description}" />

事件绑定

数据绑定省去了findViewById()setText(), 事件绑定则是省去了setOnClickListener().

比如taskdetail_frag.xml中的

<CheckBoxandroid:id="@+id/task_detail_complete"...android:checked="@{task.completed}"android:onCheckedChanged="@{(cb, isChecked) ->presenter.completeChanged(task, isChecked)}" />

其中Presenter是这时候传入的:

@Override
public void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);mViewDataBinding.setPresenter(mPresenter);
}

数据监听

在显示List数据的界面TasksFragment, 仅需要知道数据是否为空, 所以它使用了TasksViewModel来给layout提供信息, 当尺寸设定的时候, 只有一些相关的属性被通知, 和这些属性绑定的UI元素被更新.

public void setTaskListSize(int taskListSize) {mTaskListSize = taskListSize;notifyPropertyChanged(BR.noTaskIconRes);notifyPropertyChanged(BR.noTasksLabel);notifyPropertyChanged(BR.currentFilteringLabel);notifyPropertyChanged(BR.notEmpty);notifyPropertyChanged(BR.tasksAddViewVisible);
}

其他实现细节

  • Adapter中的Data Binding, 见TasksFragment中的TasksAdapter.

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {Task task = getItem(i);TaskItemBinding binding;if (view == null) {// InflateLayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());// Create the bindingbinding = TaskItemBinding.inflate(inflater, viewGroup, false);} else {binding = DataBindingUtil.getBinding(view);}// We might be recycling the binding for another task, so update it.// Create the action handler for the viewTasksItemActionHandler itemActionHandler =new TasksItemActionHandler(mUserActionsListener);binding.setActionHandler(itemActionHandler);binding.setTask(task);binding.executePendingBindings();return binding.getRoot();
    }
  • Presenter可能会被包在ActionHandler中, 比如TasksItemActionHandler.
  • ViewModel也可以作为View接口的实现, 比如StatisticsViewModel.
  • SwipeRefreshLayoutDataBinding类定义的onRefresh()动作绑定.

todo-mvp-clean

这个例子是基于Clean Architecture的原则:
The Clean Architecture.
关于Clean Architecture, 还可以看这个Sample App: Android-CleanArchitecture.

这个例子在todo-mvp的基础上, 加了一层domain层, 把应用分为了三层:


mvp-clean.png

Domain: 盛放了业务逻辑, domain层包含use cases或者interactors, 被应用的presenters使用. 这些use cases代表了所有从presentation层可能进行的行为.

关键概念
和基本的mvp sample最大的不同就是domain层和use cases. 从presenters中抽离出来的domain层有助于避免presenter中的代码重复.

Use cases定义了app需要的操作, 这样增加了代码的可读性, 因为类名反映了目的.

Use cases对于操作的复用来说也很好. 比如CompleteTask在两个Presenter中都用到了.

Use cases的执行是在后台线程, 使用command pattern. 这样domain层对于Android SDK和其他第三方库来说都是完全解耦的.

Diff with todo-mvp

每一个feature的包下都新增了domain层, 里面包含了子目录model和usecase等.

UseCase是一个抽象类, 定义了domain层的基础接口点.
UseCaseHandler用于执行use cases, 是一个单例, 实现了command pattern.
UseCaseThreadPoolScheduler实现了UseCaseScheduler接口, 定义了use cases执行的线程池, 在后台线程异步执行, 最后把结果返回给主线程.
UseCaseScheduler通过构造传给UseCaseHandler.
测试中用了UseCaseScheduler的另一个实现TestUseCaseScheduler, 所有的执行变为同步的.

Injection类中提供了多个Use cases的依赖注入, 还有UseCaseHandler用来执行use cases.

Presenter的实现中, 多个use cases和UsseCaseHandler都由构造传入, 执行动作, 比如更新一个task:

private void updateTask(String title, String description) {if (mTaskId == null) {throw new RuntimeException("updateTask() was called but task is new.");}Task newTask = new Task(title, description, mTaskId);mUseCaseHandler.execute(mSaveTask, new SaveTask.RequestValues(newTask),new UseCase.UseCaseCallback<SaveTask.ResponseValue>() {@Overridepublic void onSuccess(SaveTask.ResponseValue response) {// After an edit, go back to the list.mAddTaskView.showTasksList();}@Overridepublic void onError() {showSaveError();}});
}

todo-mvp-dagger

关键概念:
dagger2 是一个静态的编译期依赖注入框架.
这个例子中改用dagger2实现依赖注入. 这样做的主要好处就是在测试的时候我们可以用替代的modules. 这在编译期间通过flavors就可以完成, 或者在运行期间使用一些调试面板来设置.

Diff with todo-mvp

Injection类被删除了.
添加了5个Component, 四个feature各有一个, 另外数据对应一个: TasksRepositoryComponent, 这个Component被保存在Application里.

数据的module: TasksRepositoryModulemockprod目录下各有一个.

对于每一个feature的Presenter的注入是这样实现的:
首先, 把Presenter的构造函数标记为@Inject, 然后在Activity中构造component并注入到字段:

@Inject AddEditTaskPresenter mAddEditTasksPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.addtask_act);.....// Create the presenterDaggerAddEditTaskComponent.builder().addEditTaskPresenterModule(new AddEditTaskPresenterModule(addEditTaskFragment, taskId)).tasksRepositoryComponent(((ToDoApplication) getApplication()).getTasksRepositoryComponent()).build().inject(this);
}

这个module里provide了view和taskId:

@Module
public class AddEditTaskPresenterModule {private final AddEditTaskContract.View mView;private String mTaskId;public AddEditTaskPresenterModule(AddEditTaskContract.View view, @Nullable String taskId) {mView = view;mTaskId = taskId;}@ProvidesAddEditTaskContract.View provideAddEditTaskContractView() {return mView;}@Provides@NullableString provideTaskId() {return mTaskId;}
}

注意原来构造方法里调用的setPresenter方法改为用方法注入实现:

/*** Method injection is used here to safely reference {@code this} after the object is created.* For more information, see Java Concurrency in Practice.*/
@Inject
void setupListeners() {mAddTaskView.setPresenter(this);
}

todo-mvp-contentproviders

这个例子是基于todo-mvp-loaders的, 用content provider来获取repository中的数据.


mvp-contentproviders

使用Content Provider的优势是:

  • 管理了结构化数据的访问;
  • Content Provider是跨进程访问数据的标准接口.

Diff with todo-mvp-loaders

注意这个例子是唯一一个不基于最基本的todo-mvp, 而是基于todo-mvp-loaders. (但是我觉得也可以认为是直接从todo-mvp转化的.)
看diff: git difftool -d todo-mvp-loaders.

去掉了TaskLoaderTasksLoader. (回归到了基本的todo-mvp).

TasksRepository中的方法不是同步方法, 而是异步加callback的形式. (回归到了基本的todo-mvp).

TasksLocalDataSource中的读方法都变成了空实现, 因为Presenter现在可以自动收到数据更新.

新增LoaderProvider用来创建Cursor Loaders, 有两个方法:

// 返回特定fiter下或全部的数据
public Loader<Cursor> createFilteredTasksLoader(TaskFilter taskFilter)// 返回特定id的数据
public Loader<Cursor> createTaskLoader(String taskId)

其中第一个方法的参数TaskFilter, 用来指定过滤的selection条件, 也是新增类.

LoaderManagerLoaderProvider都是由构造传入Presenter, 在回调onTaskLoaded()onTasksLoaded()中init loader.

TasksPresenter中还做了判断, 是init loader还是restart loader:

@Override
public void onTasksLoaded(List<Task> tasks) {// we don't care about the result since the CursorLoader will load the data for usif (mLoaderManager.getLoader(TASKS_LOADER) == null) {mLoaderManager.initLoader(TASKS_LOADER, mCurrentFiltering.getFilterExtras(), this);} else {mLoaderManager.restartLoader(TASKS_LOADER, mCurrentFiltering.getFilterExtras(), this);}
}

其中initLoader()和restartLoader()时传入的第二个参数是一个bundle, 用来指明过滤类型, 即是带selection条件的数据库查询.

同样是在onLoadFinshed()的时候做View处理, 以TaskDetailPresenter为例:

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {if (data != null) {if (data.moveToLast()) {onDataLoaded(data);} else {onDataEmpty();}} else {onDataNotAvailable();}
}

数据类Task中新增了静态方法从Cursor转为Task, 这个方法在Presenter的onLoadFinished()和测试中都用到了.

public static Task from(Cursor cursor) {String entryId = cursor.getString(cursor.getColumnIndexOrThrow(TasksPersistenceContract.TaskEntry.COLUMN_NAME_ENTRY_ID));String title = cursor.getString(cursor.getColumnIndexOrThrow(TasksPersistenceContract.TaskEntry.COLUMN_NAME_TITLE));String description = cursor.getString(cursor.getColumnIndexOrThrow(TasksPersistenceContract.TaskEntry.COLUMN_NAME_DESCRIPTION));boolean completed = cursor.getInt(cursor.getColumnIndexOrThrow(TasksPersistenceContract.TaskEntry.COLUMN_NAME_COMPLETED)) == 1;return new Task(title, description, entryId, completed);
}

另外一些细节:
数据库中的内存cache被删了.
Adapter改为继承于CursorAdapter.

单元测试

新增了MockCursorProvider类, 用于在单元测试中提供数据.
其内部类TaskMockCursor mock了Cursor数据.
Presenter的测试中仍然mock了所有构造传入的参数, 然后准备了mock数据, 测试的逻辑主要还是拿到数据后的view操作, 比如:

@Test
public void loadAllTasksFromRepositoryAndLoadIntoView() {// When the loader finishes with tasks and filter is set to allwhen(mBundle.getSerializable(TaskFilter.KEY_TASK_FILTER)).thenReturn(TasksFilterType.ALL_TASKS);TaskFilter taskFilter = new TaskFilter(mBundle);mTasksPresenter.setFiltering(taskFilter);mTasksPresenter.onLoadFinished(mock(Loader.class), mAllTasksCursor);// Then progress indicator is hidden and all tasks are shown in UIverify(mTasksView).setLoadingIndicator(false);verify(mTasksView).showTasks(mShowTasksArgumentCaptor.capture());
}

todo-mvp-rxjava

关于这个例子, 之前看过作者的文章: Android Architecture Patterns Part 2:
Model-View-Presenter,
这个文章上过Android Weekly Issue #226.

这个例子也是基于todo-mvp, 使用RxJava处理了presenter和数据层之间的通信.

MVP基本接口改变

BasePresenter接口改为:

public interface BasePresenter {void subscribe();void unsubscribe();
}

View在onResume()的时候调用Presenter的subscribe(); 在onPause()的时候调用presenter的unsubscribe().

如果View接口的实现不是Fragment或Activity, 而是Android的自定义View, 那么在Android View的onAttachedToWindow()onDetachedFromWindow()方法里分别调用这两个方法.

Presenter中保存了:

private CompositeSubscription mSubscriptions;

subscribe()的时候, mSubscriptions.add(subscription);;
unsubscribe()的时候, mSubscriptions.clear(); .

Diff with todo-mvp

数据层暴露了RxJava的Observable流作为获取数据的方式, TasksDataSource接口中的方法变成了这样:

Observable<List<Task>> getTasks();Observable<Task> getTask(@NonNull String taskId);

callback接口被删了, 因为不需要了.

TasksLocalDataSource中的实现用了SqlBrite, 从数据库中查询出来的结果很容易地变成了流:

@Override
public Observable<List<Task>> getTasks() {...return mDatabaseHelper.createQuery(TaskEntry.TABLE_NAME, sql).mapToList(mTaskMapperFunction);
}

TasksRepository中整合了local和remote的data, 最后把Observable返回给消费者(Presenters和Unit Tests). 这里用了.concat().first()操作符.

Presenter订阅TasksRepository的Observable, 然后决定View的操作, 而且Presenter也负责线程的调度.
简单的比如AddEditTaskPresenter中:

@Override
public void populateTask() {if (mTaskId == null) {throw new RuntimeException("populateTask() was called but task is new.");}Subscription subscription = mTasksRepository.getTask(mTaskId).subscribeOn(mSchedulerProvider.computation()).observeOn(mSchedulerProvider.ui()).subscribe(new Observer<Task>() {@Overridepublic void onCompleted() {}@Overridepublic void onError(Throwable e) {if (mAddTaskView.isActive()) {mAddTaskView.showEmptyTaskError();}}@Overridepublic void onNext(Task task) {if (mAddTaskView.isActive()) {mAddTaskView.setTitle(task.getTitle());mAddTaskView.setDescription(task.getDescription());}}});mSubscriptions.add(subscription);
}

StatisticsPresenter负责统计数据的显示, TasksPresenter负责过滤显示所有数据, 里面的RxJava操作符运用比较多, 可以看到链式操作的特点.

关于线程调度, 定义了BaseSchedulerProvider接口, 通过构造函数传给Presenter, 然后实现用SchedulerProvider, 测试用ImmediateSchedulerProvider. 这样方便测试.


http://chatgpt.dhexx.cn/article/uZAsOfj4.shtml

相关文章

MVP模式的相关知识

MVP 是从经典的模式MVC演变而来&#xff0c;它们的基本思想有相通的地方&#xff1a;Controller/Presenter负责逻辑的处理&#xff0c;Model提供数据&#xff0c;View负责显示。作为一种新的模式&#xff0c;MVP与MVC有着一个重大的区别&#xff1a;在MVP中View并不直接使用Mod…

【iOS】MVP模式

文章目录 什么是MVP模式&#xff1f;图解 从MVC到MVP苹果的MVC为何要从MVC到MVP?MVP MVP模式下的工程MVP模式的优缺点 什么是MVP模式&#xff1f; MVP模式是MVC模式的一个演化版本&#xff0c;MVP全称Model-View-Presenter。&#xff08;关于MVC模式可见这篇文章&#xff09; …

浅谈安卓中的MVP模式

端午放假&#xff0c;天气下雨&#xff0c;于是乎在家撸一下博客&#xff0c;本篇博客将为大家解析MVP模式在安卓中的应用。 本文将从以下几个方面对MVP模式进行讲解&#xff1a; 1. MVP简介 2. 为什么使用MVP模式 3. MVP模式实例 4. MVP中的内存泄露问题 1. MVP简介&…

Android MVP模式 入门

1.前言 近些年来&#xff0c;Android架构模式有很多&#xff0c;我们比较熟知的有MVC&#xff0c;MVP以及MVVM&#xff0c;目前Android市场中使用最多的应该是MVP架构&#xff0c;虽然MVVM结合DataBing看似更加方便&#xff0c;但在一般公司中使用的还是比较少。其实模式这种东…

MVP模式实例解释

为什么在UI层包含太多的逻辑是很糟糕的&#xff1f;在既不手动运行应用程序&#xff0c;也不维护丑陋的自动执行UI组件的UI运行者脚本(runner script)的情况下&#xff0c;位于应用程序UI层中的代码是非常难于调试的。虽然这本身就是一个很大的问题&#xff0c;一个更大的问题是…

Android开发之MVP模式

前言&#xff1a;在之前的开发中一直用的是mvc模式搭建的项目&#xff0c;所以我对于mvp也一直只是停留在理论和demo阶段上。正好现在的项目是被小伙伴借助dragger搭建的mvp模式的结构&#xff0c;所以就想着总结整理一下mvp模式的东西并写出来&#xff0c;也算是作为自己使用了…

MVP模式与MVC模式

源地址&#xff1a;http://www.cnblogs.com/cuihongyu3503319/archive/2009/01/09/1372820.html MVP模式与MVC模式(转) MVP 是从经典的模式MVC演变而来&#xff0c;它们的基本思想有相通的地方&#xff1a;Controller/Presenter负责逻辑的处理&#xff0c;Model提供数据&#x…

MVP模式从入门到精通

首先附上自己写的一个MVP的demo&#xff0c;这是一个很标准的MVP&#xff0c;Github地址如下&#xff1a; https://github.com/SilasGao/MVPDemo 首先MVP 是从经典的MVC架构演变而来&#xff0c;那我们是不是要先说下何为MVC模式&#xff1f; 系统C/S(Client/Server)三层架构模…

MVP模式使用示例详解

什么是MVP模式? 这个MVP可不是腾讯游戏《王者荣耀》中的MVP。我们今天要讨论的MVP其实同MVC一样&#xff0c;是一种编程模式和思想&#xff0c;也许更准确地讲是一种架构。 MVP和MVC的区别 提到MVP模式&#xff0c;大家自然避免不了要和我们以前常用的MVC模式进行对…

MVP设计模式

Model–view–presenter (MVP) 是model–view–controller (MVC)设计模式派生出来的。MVP经常用来创建用户界面。 presenter是作为一个“中间人”的角色存在。在MVP中&#xff0c;所有页面显示逻辑都会被推送到presenter。 以下这张图是MVC模式的&#xff1a; MVP与MVC有着一…

Android中用到的MVP模式

参考&#xff1a;android架构设计—mvp模式封装 很简单&#xff0c;M&#xff1a;数据&#xff0c; V:界面&#xff0c; P:一个使唤数据(M)和界面(V)干活的大管家。 特点&#xff1a;在P的管理下&#xff0c;P可以直接支配V和M做一些事情。但是V&#xff0c;与M&#xff0c;你…

Android MVP模式 简单易懂的介绍方式

Android MVP Pattern Android MVP 模式1 也不是什么新鲜的东西了&#xff0c;我在自己的项目里也普遍地使用了这个设计模式。当项目越来越庞大、复杂&#xff0c;参与的研发人员越来越多的时候&#xff0c;MVP 模式的优势就充分显示出来了。 导读&#xff1a;MVP模式是MVC模式在…

Android MVP模式详解

一、MVP概述 MVP&#xff0c;全称 Model-View-Presenter&#xff0c;即模型-视图-层现器。 提到MVP&#xff0c;就必须要先介绍一下它的前辈MVC&#xff0c;因为MVP正是基于MVC的基础发展而来的。两个之间的关系也是源远流长。 MVC&#xff0c;全称Model-View-Controller&am…

浅谈安卓MVP模式

本篇博文通过对google官方demo&#xff1a;https://github.com/googlesamples/android-architecture/tree/todo-mvp/的理解&#xff0c;用自己的demo更好的讲解mvp的概念&#xff0c;帮助大家如何针对一个Activity页面去编写针对MVP风格的代码。 一、MVP模式介绍 随着UI创建技…

简单易懂 MVP 模式

Android MVP 模式 [1] 也不是什么新鲜的东西了&#xff0c;我在自己的项目里也普遍地使用了这个设计模式。当项目越来越庞大、复杂&#xff0c;参与的研发人员越来越多的时候&#xff0c;MVP 模式 的优势就充分显示出来了。 MVP 模式是 MVC 模式在 Android 上的一种变体&#…

深入浅出——MVP模式

由于公司里的架构模式用到MVP&#xff0c;觉得自己还不够熟悉&#xff0c;决定在此理一理&#xff0c;并给大家一起总结下。 一 MVP模式介绍 MVP全称Model View Presenter。 MVP能够有效的降低View的复杂性&#xff0c;避免业务逻辑被塞进View中&#xff0c;防止View的代码变…

MVP模式简单讲解,通俗易懂

了解 MVP 和 MVC 的区别 https://baike.baidu.com/item/MVP/3714550?fraladdin 什么是MVP&#xff1a; MVP 是 MVC 的变种&#xff0c;其实是一种升级。要说 MVP 就要说说 MVC&#xff0c;在 MVC 中 Activity 其实是 View层级&#xff0c;但是通常在使用中 Activity即是View…

MVP框架模式

一、基本概念 MVP是Model-View-Presenter的简称&#xff0c;即模型-视图-表现层的缩写。MVP是由MVC模式进化而来的&#xff0c;MVP改进了MVC中的控制器过于臃肿的问题。 与MVC一样&#xff0c;MVP将应用程序的数据处理、数据显示和逻辑控制分开&#xff0c;用一种业务逻辑、数…

分析几个面试题:==和===;绑定事件;正则表达式

今天也是我学后端的朋友给我发了三个前端的面试题&#xff0c;这里我们试着分析一波。 目录 1、和的含义是什么&#xff0c;又有什么区别呢&#xff1f; &#xff08;1&#xff09;赋值&#xff1a; &#xff08;2&#xff09;相同&#xff1a; &#xff08;3&#xff09;…

前端面经总结

HTML及浏览器 对栅栏布局的理解 栅格化布局中的元素&#xff1a;column列&#xff0c;row行&#xff0c;gutter列之间的距离&#xff0c;container容器 栅格是否可以嵌套 canvas和svg的区别 SVG&#xff1a; SVG 是一种使用 XML 描述 2D 图形的语言。 在 SVG 中&#xff0c;…