Android开发MVP模式实践

现在用一个基于MVP模式的APP项目进一步分析MVP的实际应用。

原项目应该使用的是Android studio开发,笔者对项目进行了整理,广大Eclipser请猛点Github链接

一、项目功能说明

APP获取好友列表后将数据展示在一个ListView中,点击Item会打开一个新页面展示好友详细信息。

二、项目结构

示例将代码分为四层,对应到MVP模式中:

  • Mode:Entities
  • Presenter:Use Cases+Presenters
  • View:UI

为了保证每个层都能方便的进行单元测试并和其它层相对独立,将项目分为三个模块,Presentation、Domain和Data。

1)Presentation

Presenters和UI被划分到这一层,但Presenters在这里只是负责将Domain逻辑处理后的数据进行组装并调度UI显示,没有业务处理逻辑。这样将数据逻辑处理划分给Domain可以让Presenter更关注于UI显示的调度,从而避免Present逻辑的冗余。这也是我选择这个工程作为示例的原因。

2)Domain

这里对Data中的数据进行逻辑处理,为Present提供业务逻辑和数据支持。

3)Data

数据仓库。例如,当通过id获取用户数据时,首先会检测用户信息是否已经存储在本地,否则的话就会从服务器获取后在本地缓存。根据上篇博客提到的设计原则,外圆代码逻辑无需关心用户数据是从存储介质、内存还是服务器获取,只需拿到Domain处理好的最终数据进行展示和交互即可。

根据以上说明,笔者将APP分为三个工程

其中Domain和Data作为Library的形式供Presentation引入。

三、代码详解

通过获取用户详细信息这个功能分析各个层级之间的调度及数据传递方向和方式。

首先入口View层的UserDetaisFragment。

[java] 
view plain
copy

  1. public class UserDetailsFragment extends BaseFragment implements UserDetailsView

[java] 
view plain
copy

  1. private UserDetailsPresenter userDetailsPresenter;

[java] 
view plain
copy

  1. @Override void initializePresenter() {
  2. // All these dependency initialization could have been avoided using a
  3. // dependency injection framework. But in this case are used this way for
  4. // LEARNING EXAMPLE PURPOSE.
  5. ThreadExecutor threadExecutor = JobExecutor.getInstance();
  6. PostExecutionThread postExecutionThread = UIThread.getInstance();
  7. JsonSerializer userCacheSerializer = new JsonSerializer();
  8. UserCache userCache = UserCacheImpl.getInstance(getActivity(), userCacheSerializer,
  9. FileManager.getInstance(), threadExecutor);
  10. UserDataStoreFactory userDataStoreFactory =
  11. new UserDataStoreFactory(this.getContext(), userCache);
  12. UserEntityDataMapper userEntityDataMapper = new UserEntityDataMapper();
  13. UserRepository userRepository = UserDataRepository.getInstance(userDataStoreFactory,
  14. userEntityDataMapper);
  15. GetUserDetailsUseCase getUserDetailsUseCase = new GetUserDetailsUseCaseImpl(userRepository,
  16. threadExecutor, postExecutionThread);
  17. UserModelDataMapper userModelDataMapper = new UserModelDataMapper();
  18. this.userDetailsPresenter =
  19. new UserDetailsPresenter(this, getUserDetailsUseCase, userModelDataMapper);
  20. }

UserDetailsFragment内部有个UserDetailsPresenter引用,下面我们看UserDetailsPresenter代码。

[java] 
view plain
copy

  1. public class UserDetailsPresenter implements Presenter {
  2. /** id used to retrieve user details */
  3. private int userId;
  4. private final UserDetailsView viewDetailsView;
  5. private final GetUserDetailsUseCase getUserDetailsUseCase;
  6. private final UserModelDataMapper userModelDataMapper;
  7. public UserDetailsPresenter(UserDetailsView userDetailsView,
  8. GetUserDetailsUseCase getUserDetailsUseCase, UserModelDataMapper userModelDataMapper) {
  9. if (userDetailsView == null || getUserDetailsUseCase == null || userModelDataMapper == null) {
  10. throw new IllegalArgumentException("Constructor parameters cannot be null!!!");
  11. }
  12. this.viewDetailsView = userDetailsView;
  13. this.getUserDetailsUseCase = getUserDetailsUseCase;
  14. this.userModelDataMapper = userModelDataMapper;
  15. }

[java] 
view plain
copy

  1. private void showViewLoading() {
  2. this.viewDetailsView.showLoading();
  3. }
  4. private void getUserDetails() {
  5. this.getUserDetailsUseCase.execute(this.userId, this.userDetailsCallback);
  6. }
  7. private final GetUserDetailsUseCase.Callback userDetailsCallback = new GetUserDetailsUseCase.Callback() {
  8. @Override public void onUserDataLoaded(User user) {
  9. UserDetailsPresenter.this.showUserDetailsInView(user);
  10. UserDetailsPresenter.this.hideViewLoading();
  11. }
  12. @Override public void onError(ErrorBundle errorBundle) {
  13. UserDetailsPresenter.this.hideViewLoading();
  14. UserDetailsPresenter.this.showErrorMessage(errorBundle);
  15. UserDetailsPresenter.this.showViewRetry();
  16. }
  17. };
  18. }
  19. private void hideViewLoading() {
  20. this.viewDetailsView.hideLoading();
  21. }
  22. private void showViewRetry() {
  23. this.viewDetailsView.showRetry();
  24. }
  25. private void hideViewRetry() {
  26. this.viewDetailsView.hideRetry();
  27. }

Presenter包涵一个GetUserDetailsUseCase引用,并在调用getUserDetails()方法时传入一个callBack作为数据处理结果的回调。可以看到Presenter中并没有复杂的逻辑处理,反而更多的是hideViewRetry(),showViewLoading()等对Fragemnt的显示调度方法。Fragemt实现了UserDetailsView接口协议并在实例化Presenter时当做参数传入,这样就搭建好了符合MVP模式的Presenter和View的交互方式。

[java] 
view plain
copy

  1. public interface UserDetailsView extends LoadDataView {
  2. /**
  3. * Render a user in the UI.
  4. *
  5. * @param user The {@link UserModel} that will be shown.
  6. */
  7. void renderUser(UserModel user);
  8. }

[java] 
view plain
copy

  1. public interface LoadDataView {
  2. /**
  3. * Show a view with a progress bar indicating a loading process.
  4. */
  5. void showLoading();
  6. /**
  7. * Hide a loading view.
  8. */
  9. void hideLoading();
  10. /**
  11. * Show a retry view in case of an error when retrieving data.
  12. */
  13. void showRetry();
  14. /**
  15. * Hide a retry view shown if there was an error when retrieving data.
  16. */
  17. void hideRetry();
  18. /**
  19. * Show an error message
  20. *
  21. * @param message A string representing an error.
  22. */
  23. void showError(String message);
  24. /**
  25. * Get a {@link android.content.Context}.
  26. */
  27. Context getContext();
  28. }

下面我们看GetUserDetailUseCase代码。GetUserDetailUseCase在Domain模块,负责加工处理Data模块的数据。

[java] 
view plain
copy

  1. public class GetUserDetailsUseCaseImpl implements GetUserDetailsUseCase {

[java] 
view plain
copy

  1. private final UserRepository userRepository;

[java] 
view plain
copy

  1. @Override public void execute(int userId, Callback callback) {
  2. if (userId < 0 || callback == null) {
  3. throw new IllegalArgumentException("Invalid parameter!!!");
  4. }
  5. this.userId = userId;
  6. this.callback = callback;
  7. this.threadExecutor.execute(this);
  8. }

[java] 
view plain
copy

  1. @Override public void run() {
  2. this.userRepository.getUserById(this.userId, this.repositoryCallback);
  3. }

[java] 
view plain
copy

  1. private final UserRepository.UserDetailsCallback repositoryCallback =
  2. new UserRepository.UserDetailsCallback() {
  3. @Override public void onUserLoaded(User user) {
  4. notifyGetUserDetailsSuccessfully(user);
  5. }
  6. @Override public void onError(ErrorBundle errorBundle) {
  7. notifyError(errorBundle);
  8. }
  9. };
  10. private void notifyGetUserDetailsSuccessfully(final User user) {
  11. this.postExecutionThread.post(new Runnable() {
  12. @Override public void run() {
  13. callback.onUserDataLoaded(user);
  14. }
  15. });
  16. }

[java] 
view plain
copy

  1. }

GetUserDetailUseCaseImpl又会调用Data模块的UserRepository获取用户数据。UserRepository会从本地存储或服务器获取用户数据,这里就不再跟进UserRepository的源码。

当GetUserDetailUseCaseImpl从UserRepository获取用户数据后通过UserRepository.UserDetailCallback回调给UserDetailPresenter,然后Presenter就会根据数据调度Fragment的显示。
下面总结下从用户点击Item到打开用户详情页的调用流程及数据流动方向

用户点击Fragment中ListView的Item,Fragment向Presentor询问用户详情信息,Presentor将命令传递给UseCase,UseCase从Data获取数据并加工后通过callBack将数据传递回Presentor,Presentor最终告诉Fragment打开新页面并展示数据。

大家下载源码后会发现甭管是Presentor和UI,还是UseCase和Data,都是通过接口协议进行交互,这也是MVP模式的特点之一。

MVP就分析到这里,我认识的也很有限,欢迎大家讨论指正。

参考资料:

MVC or MVP Pattern - Whats the difference?

时间: 2024-10-09 11:22:03

Android开发MVP模式实践的相关文章

ym——Android开发MVP模式(解决了View和Model的耦合)

什么是MVP呢?它又和我们常常听到的MVC有什么关系了以及区别呢? MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示.作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不

android开发-mvp模式理解

看之前,先忘掉所有,一步步看就行了. 最后会有一个原型demo,当然是转的了.看完文章,再看demo,然后再回头看文章就很好理解了,最好自己写一遍. 1.mvp开发模式可以理解为页面接口编程,每一层的骨架都是先通过建立接口,定义每层的必要方法,再实现方法完成的. 2.在mvp开发模式中,把每个模块代码分为三个层,View,presenter,model层. 3.view层从字面意义上理解,就是视图层,在安卓的mvp开发模式中,把activity,fragment,等 一些界面显示看作是视图层,在

Android -- 初探MVP模式

1,相信大家对mvp模式都很熟悉了,M-Model-模型.V-View-视图.C-Controller-控制器.MVP作为MVC的版本演化,与MVC的意义类似:M-Model-模型.V-View-视图.P-Presenter-表示器.从MVC和MVP两者结合来看,Controlller/Presenter在MVC/MVP中都起着逻辑控制处理的角色,起着控制各业务流程的作用.而 MVP与MVC最不同的一点是M与V是不直接关联的也是就Model与View不存在直接关系,这两者之间间隔着的是Prese

Android设计模式-MVP模式

一.什么是MVP模式 MVP(Model / View / Presenter)  是从经典的模式MVC演变而来,Presenter代替activity和fragment成为控制器,而activity和fragment专心做View层该做的事. 二.MVP的优点 1.模型和视图分离开了,层次更清晰了. 2.Presenter可以重复利用了. 3.如果我们把逻辑放在Presenter中,单元测试更简单了 三.实战 下边是我项目的结构图 MVP中parsenter处理完逻辑后通过接口通知View层,

Android开发和测试实践 - 接入友盟统计

这两年一直在做无线的测试,后续还会继续去做无线的测试,但是之前因为时间的原因一直都没有非常仔细的了解到代码层面.近期抽空自己做了些app的开发,决定如果想把移动的测试做好做深入,有一定的app开发经验非常的有必要,因为只有这样很多东西才能真正理解了.另一个方面,这本身也很有趣.目前尝试的一些东西都还比较基础,准备持续的做下去. 实践的过程也想陆续整理出来,做下笔记,也给别人一些参考,遇到很多问题的时候也是到谷歌度娘的找答案,帮助很多.目前更多关注android,ios的部分稍继续. 基本上现在任

[转]MVP模式开发

转自:http://www.jianshu.com/p/f7ff18ac1c31 基于面向协议MVP模式下的软件设计-(iOS篇) 字数9196 阅读505 评论3 喜欢11 基于面向协议MVP模式下的软件设计-(iOS篇) 传统模式下的开发 MVC MVVM 基于面向协议MVP的介绍 MVP实战开发 说在前面:相信就算你是个iOS新手也应该听说过MVC的,MVC是构建iOS App的标准模板.随着时间的推移,在iOS平台上MVC也逐渐开始面临着越来越多的问题,最近又开始流行MVVM,MVVM使

Android架构篇--MVP模式的介绍篇

摘要: 在MVVM成熟之前MVP模式在Android上有被神化的趋势,笔者曾经在商业项目中从零开始大规模采用过MVP模式对项目进行开发.在使用MVP模式进行开发的时候发现项目的结构模式对开发是有一定的影响的,在这里笔者会对这一问题进行探讨.希望通过这篇blog能让读者了解如何使用MVP模式搭建一个功能完善的MVP模式开发框架,避免一些笔者认为比较严重的问题. 为什么要使用MVP模式 在传统的Android开发中,我们一般是使用MVC模式进行开发的.传统MVC模式介绍: View: 视图层,对应x

MVP模式在Android开发中的最佳实践

这篇文章拖了好久了,一直存在草稿箱里没有继续写,趁今天有空,撸撸完. 回想一下,你刚刚学习Android的时候,总会看到一些书上写着,Android使用的是MVC模式,Activity就是一个Controller,或许那个时候,你没有什么深刻的体会.随着经验的积累.你发现,Activity既是Controller,掌管着许许多多的业务逻辑,同时它也作为View的一部分,控制着视图层的显示.久而久之,这个Controller便显得过于重,职责不再那么单一. 于是,再后来,为了使Activity的职

MVP模式在Android开发中的应用

一.MVP介绍 随着UI创建技术的功能日益增强,UI层也履行着越来越多的职责.为了更好地细分视图(View)与模型(Model)的功能,让View专注于处理数据的可视化以及与用户的交互.同一时候让Model仅仅关系数据的处理.基于MVC概念的MVP(Model-View-Presenter)模式应运而生. 在MVP模式里通常包括4个要素: (1)View:负责绘制UI元素.与用户进行交互(在Android中体现为Activity); (2)View interface:须要View实现的接口,V