MVP(2)- 浅析如何高效的使用MVP

博客源址:http://blog.csdn.net/dantestones/article/details/51445208

Android mvp 架构的自述中我简单的介绍了mvp,以及怎么写mvp。我自己也将mvp运用到了项目中,其实mvp并没有固定的写法,正确的去理解架构的思想,都可以有自己独特的mvp写法。git上也有很多例子,比如google的android-architecture,simple哥的Android 源码设计模式解析与实战中也有mvp的讨论。这里参考了simple哥做了一个通用版的mvp,并对google的MVP做了一点自己的解析。

关于presenter一直持有Activity对象导致的内存泄漏问题

只要用过mvp这个问题可能很多人都知道。写mvp的时候,presenter会持有view,如果presenter有后台异步的长时间的动作,比如网络请求,这时如果返回退出了Activity,后台异步的动作不会立即停止,这里就会有内存泄漏的隐患,所以会在presenter中加入一个销毁view的方法。现在就在之前的项目中做一下修改

//presenter中添加mvpView 置为null的方法

public void onDestroy(){
    mvpView = null;
}

//退出时销毁持有Activity

@Override
protected void onDestroy() {
    mvpPresenter.onDestroy();
    super.onDestroy();
}

presenter中增加了类似的生命周期的方法,用来在退出Activity的时候取消持有Activity。

但是在销毁后需要思考一点,后台的延时操作返回时,这个时候view被销毁了,如果接着去调用view的方法就 会抛出空指针异常。所以在后台的延时操作中需要考虑到这种可能产生空指针的情况,尤其是网络请求。

BasePresenter

如果每一个Activity都需要做绑定和解绑操作就太麻烦了,现在我希望可以有一个通用的presenter来为我们添加view的绑定与销毁。

public abstract class BasePresenter<T> {   

     public T mView;    

     public void attach(T mView) {
         this.mView = mView;
     }    

     public void dettach() {
         mView = null;
     }
}

因为不能限定死传入的View,所以使用泛型来代替传入的对象。通过这个通用的presenter我就可以把原来的MvpPresenter简化成下面的样子

public class NewMvpPresenter extends BasePresenter<NewMvpView> {   

    private RequestBiz requestBiz;
    private Handler mHandler;    

    public NewMvpPresenter() {
        requestBiz = new RequestBiziml();
        mHandler = new Handler(Looper.getMainLooper());
   }    

    public void onResume(){
        requestBiz.requestForData(new OnRequestListener() {
             @Override
             public void onSuccess(final List<String> data) {
                 mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mView.hideLoading();
                        mView.setListItem(data);
                   }
               });
            }            

             @Override
             public void onFailed() {
                 mView.showMessage("请求失败");
             }
        });
      }    

    public void onItemClick(int position){
        mView.showMessage("点击了item"+position);
    }
}

BaseView

界面需要提供的UI方法中会有很多类似的UI方法,可以把它们提取到一个公共的父类接口中。比如提取显示loading界面和隐藏loading界面的方法,其他的view层接口就可以直接继承BaseView接口,不必重复的写显示和隐藏loading界面方法。

public interface BaseView {
    void showLoading();
    void hideLoading();
}

BaseMvpActivity

presenter绑定到activity和View的绑定和解绑操作是每个Activity都会去做的,同样这里我也希望能有一个父类来完成这个统一的操作。

public abstract class BaseMvpActivity<V,T extends BasePresenter<V>> extends AppCompatActivity {   

    public T presenter;    

     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         presenter = initPresenter();
     }    

     @Override
     protected void onResume() {
          super.onResume();
          presenter.attach((V)this);
     }    

     @Override
     protected void onDestroy() {
        presenter.dettach();
        super.onDestroy();
     }   

     // 实例化presenter
     public abstract T initPresenter();

}

同样使用泛型来提取通用的逻辑,presenter的初始化,以及view的绑定和解绑操作都提取到父类Activity中。向外部提供了一个 initPresenter(); 方法用来初始化presenter,如果想创建不同参数的构造函数都可以随意去创建。

更加通用的例子

通过上面的base父类,对之前的例子进行优化,写一个更加好用的例子。

NewMvpView 继承BaseView接口,添加自己的初始化ListView和Toast信息方法
public interface NewMvpView extends BaseView {
        void setListItem(List<String> data);
        void showMessage(String message);
}

NewMvpPresenter 继承BasePresenter类,增加网络请求和处理点击事件的方法

public class NewMvpPresenter extends BasePresenter<NewMvpView> {
        private RequestBiz requestBiz;
        private Handler mHandler;    

        public NewMvpPresenter() {
            requestBiz = new RequestBiziml();
           mHandler = new Handler(Looper.getMainLooper());
        }    

        public void onResume(){
            requestBiz.requestForData(new OnRequestListener() {
                @Override
                public void onSuccess(final List<String> data) {
                   mHandler.post(new Runnable() {
                      @Override
                       public void run() {
                           mView.hideLoading();
                           mView.setListItem(data);
                        }
                    });
                 }            

                 @Override
                 public void onFailed() {
                     mView.showMessage("请求失败");
                 }
            });
       }   

        public void onItemClick(int position){
              mView.showMessage("点击了item"+position);
        }
}

NewMvpActivity

public class NewMvpActivity extends BaseMvpActivity<NewMvpView,NewMvpPresenter> implements NewMvpView,AdapterView.OnItemClickListener{
        ListView mvpListView;
        ProgressBar pb;    

        @Override
        protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_mvp);
              mvpListView = (ListView)findViewById(R.id.mvp_listview);
              mvpListView.setOnItemClickListener(this);
              pb = (ProgressBar) findViewById(R.id.mvp_loading);
       }    

       @Override
        protected void onResume() {
             super.onResume();
             presenter.onResume();
        }    

      @Override
       public NewMvpPresenter initPresenter() {
             return new NewMvpPresenter();
       }    

      @Override
       public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                presenter.onItemClick(position);
      }    

      @Override
       public void setListItem(List<String> data) {
           ArrayAdapter adapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1,data);
           mvpListView.setAdapter(adapter);
       }    

      @Override
       public void showMessage(String message) {
           Toast.makeText(this,message,Toast.LENGTH_SHORT).show();
       }    

      @Override
       public void showLoading() {
           pb.setVisibility(View.VISIBLE);
       }    

       @Override
       public void hideLoading() {
          pb.setVisibility(View.GONE);
      }
}

最终的成果,我们只需要在Acitivity中传入泛型对象,并写好initPresenter() Presenter的初始化的方法就可以直接去使用presenter,当然View的接口还是要自己去实现。

以上的方法只是一些比较简单的封装,下面来看看官方的MVP架构是怎么写的。

官方的MVP架构

先来看看官方的代码目录(这里只是功能模块的目录,不包括测试模块,毕竟这里分析的是官方的实现代码)

这里写图片描述

谷歌的todomvp工程实现了一个类似记事本的功能,整体的目录结构:

tasks 包可以显示任务列表

taskdetail包显示任务详情

addedittask包添加和编辑任务

statistics包用来显示任务的完成情况

data包数据模块对应mvp的M

Util包就是通用的方法。

这里todomvp也有BasePresenter和BaseView2个基类,不过看的出来这2个都是接口。先来看看这2个接口

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

各自简单的声明了一个方法,start()方法用来给Presenter做一些初始化的操作不用特别在意,BaseView声明的方法就很有意思了,setPresenter(T presenter) 很明显是给View绑定Presenter,而前文我们使用的方式给Presenter传入View的方式来完成View和Presenter绑定的操作,这里谷歌采用了相反的方式来操作,为什么呢?

先把这个问题留下,看看单个模块的具体的文件结构。

这里写图片描述

有Activity,Fragment,Presenter,View哪里去了? 其实谷歌的mvp是将Fragment作为View层来实现的,这一点在官方的Readme中也有说明,为什么要用Fragment?

官方认为Fragement和Activity相比更像是MVP中的的View层,刚好可以满足MVP的View层的要求,Activity则作为最高指挥官用来创建和联系View和Presenter。

Fragment在平板或者屏幕上有多个View时更有优势

Activity只作为创建和联系View和PresenterView而存在,将Fragment作为显示UI而存在。Activity主指挥,Fragment主显示。这也是谷歌的sample中的一贯做法。

View的问题解释完了,再看看TaskDetailContract 这种**Contract 接口,这也是官方独有的管理方法

Contract

google的todomvp 工程中每个模块都会有一个 **Contract 接口,来看看他的代码

public interface TaskDetailContract {
    interface View extends BaseView<Presenter> {
        void setLoadingIndicator(boolean active);
        void showMissingTask();
        void hideTitle();
        void showTitle(String title);
        void hideDescription();
        void showDescription(String description);
        void showCompletionStatus(boolean complete);
        void showEditTask(String taskId);
        void showTaskDeleted();
        void showTaskMarkedComplete();
        void showTaskMarkedActive();
        boolean isActive();
}
    interface Presenter extends BasePresenter {
        void editTask();
        void deleteTask();
        void completeTask();
        void activateTask();
    }
}

Contract其实就是一个包涵了Presenter和View的接口,Presenter实现的逻辑层方法,View实现的UI层的方法都能在Contract接口中一目了然的看明白,具体的Presenter和View的实现类都是通过实现Contract接口来完成。这种方式既方便了管理和维护,也给开发点了一个导航灯。

下面来看看Presenter如何引用到Fragment中以及View如何与Presenter建立联系

TaskDetailActivity

/**
 * Displays task details screen.
 */
public class TaskDetailActivity extends AppCompatActivity {
    public static final String EXTRA_TASK_ID = "TASK_ID";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.taskdetail_act);

        // Set up the toolbar.
        //.... 设置toolbar 代码省略
        // Get the requested task id
        String taskId = getIntent().getStringExtra(EXTRA_TASK_ID);

        // 实例化taskDetailFragment
        TaskDetailFragment taskDetailFragment = (TaskDetailFragment) getSupportFragmentManager()
                                                     .findFragmentById(R.id.contentFrame);
        if (taskDetailFragment == null) {
            //taskDetailFragment 添加到Activity
            taskDetailFragment = TaskDetailFragment.newInstance(taskId);
            ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),
            taskDetailFragment, R.id.contentFrame);
        }

        // Create the presenter  初始化Presenter
       new TaskDetailPresenter(taskId,Injection.provideTasksRepository(getApplicationContext()),
                               taskDetailFragment);
}

….. 省去不重要代码

TaskDetailActivity主要做了taskDetailFragment 初始化和TaskDetailPresenter的初始化操作,在做TaskDetailPresenter初始化时直接将taskDetailFragment作为参数传入,因为taskDetailFragment实现了View层的接口,看到这里有一个疑问,new TaskDetailPresenter()可以实例化一个Presenter,但是怎么传入到taskDetailFragment中呢?为了解开疑惑我查看了TaskDetailPresenterde 构造方法

public class TaskDetailPresenter implements TaskDetailContract.Presenter {
     //....省去不重要的变量声明

    public TaskDetailPresenter(@Nullable String taskId,
                               @NonNull TasksRepository tasksRepository,
                               @NonNull TaskDetailContract.View taskDetailView) {
        this.mTaskId = taskId;
        mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
        mTaskDetailView = checkNotNull(taskDetailView, "taskDetailView cannot be null!");
        //实现绑定Presenter的关键方法
        mTaskDetailView.setPresenter(this);
}

关键方法就是在BaseView中声明的void setPresenter(T presenter); 方法实现的绑定,当然这里只是调用View的方法,具体的绑定还要追踪到实体类中,来看TaskDetailFragment

TaskDetailFragment

public class TaskDetailFragment extends Fragment implements TaskDetailContract.View {
     ......省去不重要代码

 //声明了一个mPresenter
    private TaskDetailContract.Presenter mPresenter;

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         View root = inflater.inflate(R.layout.taskdetail_frag, container, false);
         ....省去初始化代码
         return root;
    }

    @Override
    public void setPresenter(@NonNull TaskDetailContract.Presenter presenter) {
         //完成mPresenter的绑定
         mPresenter = checkNotNull(presenter);
    }

.....
}

TaskDetailFragment 实现了TaskDetailContract接口中的View接口,这样在TaskDetailPresenter 构造方法中调用mTaskDetailView.setPresenter(this)方法后,TaskDetailFragment 的setPresenter也会调用,TaskDetailPresenter 就成功绑定到了TaskDetailFragment 中。

这种绑定方式也解释了上文提到的问题,为什么谷歌采用了相反的方式操作,就是为了这后面的绑定操作。

Acitivity对象内存泄漏的问题

谷歌的项目同样会有当有后台异步任务时可能导致Acitivity内存泄漏的问题。谷歌也有自己的处理方法,上面提到的TaskDetailContract 接口有声明一个方法 isActive()

public interface TaskDetailContract {
    interface View extends BaseView<Presenter> {
        ....省去其他的方法
        boolean isActive();
}

看看这方法的具体实现类

public class TaskDetailFragment extends Fragment implements TaskDetailContract.View {
     .....
     @Override
     public boolean isActive() {
          return isAdded();
     }

isAdded()方法如果返回true代表Fragment添加到了Activity,false代表没有添加,通过调用isActive()方法就可以给后台异步任务添加判断避免内存泄漏。

UML结构图

到这里整体的架构就已经清楚了配上这张UML图可以更好的理解。

这里写图片描述

Activity最外层负责Presenter和View的创建和联系。

Fragment实现了View的接口并与Presenter绑定

Presenter从数据层中获取数据与View进行交互

google的todomvp不光给了我们一个mvp的架构演示,还附带了一个测试模块,整体的架构非常适合用来开发。强烈建议大家去github上下载源码来看看,效果更佳

相关代码

https://github.com/haibuzou/MVPSample

https://github.com/googlesamples/android-architecture/tree/todo-mvp/

时间: 2024-10-10 14:02:24

MVP(2)- 浅析如何高效的使用MVP的相关文章

Android开发中MVP模式浅析

目前为止,MVP的使用还没有一个标准,在此先记录一下目前学习到的一些Android中使用MVP的知识. 按传统的方式开发,经常会使Activity中混杂着UI交互,业务逻辑等流程.而MVP模式能巧妙的解决这个问题.先直接上一个小例子吧. /** * 定义一个对UI组件进行操作的接口,让Activity实现这个接口 * @author Quinn * @date 2015-5-9 */ public interface LoginView { public void showProgress();

如何更高效的使用MVP以及官方MVP架构解析

转载请标明出处: http://blog.csdn.net/dantestones/article/details/51445208 Android mvp 架构的自述中我简单的介绍了mvp,以及怎么写mvp.我自己也将mvp运用到了项目中,其实mvp并没有固定的写法,正确的去理解架构的思想,都可以有自己独特的mvp写法.git上也有很多例子,比如google的android-architecture,simple哥的Android 源码设计模式解析与实战中也有mvp的讨论.这里参考了simpl

MVP MVVM MVC

上一篇得到大家的关注,非常感谢.由于自己对于这些模式的理解也是有限,对于MVC,MVP,MVVM这些模式的比较,是结合自己的理解,一些地方不一定准确,需要的朋友可以参考下 上一篇得到大家的关注,非常感谢.由于自己对于这些模式的理解也是有限,对于这些模式的比较,是结合自己的理解,一些地方不一定准确,但是只有亮出自己的观点,才能抛砖引玉不是? 欢迎各位拍砖.:) 复制代码 代码如下: 阅读目录: 四. MVP模式 4.1 MVP的思想 4.2 UI界面接口化 4.3 Presenter —— Mod

Android App的设计架构:MVC,MVP,MVVM与架构经验谈

来源: Android App的设计架构:MVC,MVP,MVVM与架构经验谈 和MVC框架模式一样,Model模型处理数据代码不变在Android的App开发中,很多人经常会头疼于App的架构如何设计: 我的App需要应用这些设计架构吗? MVC,MVP等架构讲的是什么?区别是什么? 本文就来带你分析一下这几个架构的特性,优缺点,以及App架构设计中应该注意的问题. 1.架构设计的目的 通过设计使程序模块化,做到模块内部的高聚合和模块之间的低耦合.这样做的好处是使得程序在开发的过程中,开发人员

安卓实践开发之MVP一步步实现到高级封装

在上家干了快2年辞职后在家休息了快一个月了,说实在的不上班的感觉爽(睡觉睡到自然醒,游戏玩到手抽筋).哈哈,又是快到一年过中秋的时候了,好久没有更新博客了,今天顺便撸一篇. 前言 话说MVP的模式已经问世好几年了,为什么很多公司还是不愿意接受呢?说实在的我就还是喜欢自己的mvc,不喜欢看见mvp庞大的架构,所以前公司的项目呢也不曾使用过mvp(同事也不接受这种模式),毕竟项目架构不是特别复杂的话使用mvp显示不出他的优势,相反给人的感觉是实现一个界面多出了很多的代码. 然而现在最火的应该是mvv

MVC, MVP, MVVM比较以及区别

MVC, MVP和MVVM都是用来解决界面呈现和逻辑代码分离而出现的模式.以前只是对它们有部分的了解,没有深入的研究过,对于一些里面的概念和区别也是一知半解.现在一边查资料,并结合自己的理解,来谈一下对于这三种模式思想的理解,以及它们的区别.欢迎各位高手拍砖. 阅读目录: 一. MVC, MVP, MVVM诞生的需求? 二. 一段典型的耦合代码 三. MVC模式 3.1 主动MVC 3.2 被动MVC 3.3 Web应用中的MVC框架 3.4 MVC总结 一,MVC, MVP, MVVM诞生的需

Retrofit2.0+RxJava+MVP+Bmob的使用

本篇来总结一下学过的关于android方面的东西.梳理一下知识: 1.Retrofit2.0 Retrofit 是一个Square开发的类型安全的REST安卓客户端请求库.这个库为网络认证.API请求以及用OkHttp发送网络请求提供了强大的框架 . 2.RxJava/RxAndroid RxJava是一款响应式变成框架.RxAndroid在RxJava基础之上扩展了android线程调度.RxJava基本组成部分是Observables和Subscribers(事实上Observer才是最小的

Google官方架构MVP解析与实战进阶必学系列

1 前言 当然对于MVP的解说也是使用也是层出不穷,我也网络上也能看到各种版本的解说,之前博客也有文章的更新,里面有MVP的详细说明和项目代码--->Android中的MVP模式,带实例. 本篇文章将参考 google官方android MVP架构项目的实现,来实现自己的项目.或许看了这篇文章之后,你再去梳理一下google官方架构项目,会让你收获更多.官方的实例肯定具有更好的权威性. 推荐关注安卓各种架构相关文章合集github地址:AndroidArchitectureCollection

Cg入门10:Vertex Shader - 几何变换 —MVP矩阵变换

Unity内建矩阵类型: M:世界矩阵 V:摄像机矩阵 P:投影矩阵 T :矩阵的转置 IT : 转置的的逆 _Object2World: 模型到世界矩阵 _World2Object:世界到模型矩阵 MVP 矩阵变换:Shader Demo MVP 转置变换:C# demo Matrix4x4 mvp = Camera.main .projectionMatrix * Camera.main .worldToCameraMatrix * transform .localToWorldMatrix