结合实例分析Android MVP的实现

  最近阅读项目的源码,发现项目中有MVP的痕迹,但是自己却不能很好地理解相关的代码实现逻辑。主要原因是自己对于MVP的理解过于概念话,还没有真正操作过。本文打算分析一个MVP的简单实例,帮助自己更好的理解MVP的内在思想。

  对于什么是MVP,MVP和MVC的区别,MVP的有点,大家可以参考这篇文章:MVP 模式简单易懂的介绍方式。文章里面还有demo,可以帮助大家更好的理解。

  今天分析的是一个别人写的 demo,其实作者也有写文章来介绍(Android MVP with Fragment and RecyclerView),那我为何还要自己来分析一遍呢?其实我已经仿照这个 demo 将 MVP 的思想用到了自己写的一个 demo 上。但是,时间长了,又忘记了,所以打算梳理下。当然,文章肯定不会跟作者的文章一样,得提出自己的思想。并且还对 demo 进行改造优化,使其更加符合MVP的思想。

  推荐先看前面两篇文章,再来看本文,这样能更好的理解文章的内容。

代码结构简析

  首先我们来看代码的结构图。 从中可以看到有6个文件夹,与 MVP 模式相关的是后面三个文件夹。model 中存放的是与数据相关的类。Picture 是数据 model,其他几个类是负责下载Picture获取数据的。Presenter 中会引入 model 和 view 的引用,以此来控制 model 和 view。View中只有一个 PictureView 类,但是严格说来,应该把 PictureFragment 和 PictureAdapter 也放在文件夹 view 中,但是这样放也是可以的。

实现逻辑

总体概要

  这个项目要做的事情很简单,就是从网络下载图片,显示在手机上,点击图片,弹出一个 Toast。

思路分析

  MVP ?在这里 M 不就是图片,所以肯定会有一个 Picture 实体类。图片需要下载,因此,还要建立一个类用来控制,但是最终的调用下载是在 P 中。

  那 V ?就是 Fragment 啦,由 recyclerView 和 ProgressBar 构成的。PictureView 是一个接口,用于控制图片的展示等。但是也是在 P 中调用。

  最后就是 P 啦,封装了对 M, V 的操作,即 PicturePresenterImpl 这个类。

代码实现

  M的实现(只贴重要的代码):

public class PictureInteractorImpl implements PictureInteractor {

    private final static String[] pictureNames = {
            "Rocket in the universe",
            "A scene in London",
            "Moon over mountains",
            "A simple moon",
            "Sun and volcano",
            "A collection of mountains",
            "River between mountains",
            "Some pine trees",
            "On Small Town",
            "Volcanos reflection"
    };

    private final static int  pictureImages[] = {
            R.drawable.cohete_flat,
            R.drawable.london_flat,
            R.drawable.material_flat,
            R.drawable.moon_flat,
            R.drawable.mountain_flat,
            R.drawable.mountain_mo_flat,
            R.drawable.moutain_go_flat,
            R.drawable.pine_flat,
            R.drawable.towers_flat,
            R.drawable.vulcan_flat
    };

    @Override
    public void loadPictures(final LoaderListener listener) {
        new Handler(Looper.getMainLooper())
                .postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        listener.onFinish(createPictures());
                    }
                }, 2000);
    }

    private List<Picture> createPictures() {
        ArrayList<Picture> pictures = new ArrayList<>();
        for (int i = 0; i < pictureNames.length; i++) {
            pictures.add(new Picture(pictureNames[i], pictureImages[i]));
        }
        return pictures;
    }
}

大家看上面的代码,只有 loadPictures 是来自于接口的,为啥 createPictures 方法不写在接口里呢?主要是因为写在接口的方法是要在 P 中调用的。如果不需要外部调用,就没必要接口里面了。

  V 的实现(只贴重要的代码):

public interface PictureView {
    void showProgressBar();

    void hideProgressBar();

    void showMsg(String msg);

    void showPictures(List<Picture> pictures);
}

  虽然 View  中只有 PictureView 一个类,但是从这个类可以对 view 要做的事一清二楚。

  P 的实现(只贴重要的代码):

public class PicturePresenterImpl implements PicturePresenter, LoaderListener {

    private PictureView mPictureView;
    private PictureInteractor mInteractor;

    public PicturePresenterImpl(PictureView pictureView) {
        this.mPictureView = pictureView;
        mInteractor = new PictureInteractorImpl();
    }

    @Override
    public void onResume() {
        mPictureView.showProgressBar();
        mInteractor.loadPictures(this);
    }

    @Override
    public void onDestroy() {
        mPictureView = null;
    }

    @Override
    public void onItemClick(int pos) {
        mPictureView.showMsg(String.valueOf(pos));
    }

    @Override
    public void onFinish(List<Picture> pictures) {
        mPictureView.hideProgressBar();
        mPictureView.showPictures(pictures);
    }
}

  可以看出来,P 中其实就是对 M,V 的逻辑进行了封装,统一由其来掌控。

  最后来讲讲 fragment,内部引入了 P 。主要是由于 P 无法控制生命周期,所以需要借用 fragment 的生命周期来对整个过程进行控制。

疑问?

  大家看了上面的demo,不觉得在 fragment 中,即夹杂着 V, 又有 P,这样其实不利于维护,尤其是后期当 view 越来越多的时候,那时候,还要把 view 的初始化等等都写在 fragment 中嘛?所以接下去要对 fragment 内容进行瘦身。那怎么瘦身呢?具体请看下文。

改造

  改造后的结构,只在 view 中新建了一个 BasePageView 来处理 view 的初始化和控制逻辑。

其代码具体如下:

public class BasePageView extends FrameLayout implements PictureView {

    private RecyclerView mRecyclerView;
    private ProgressBar mProgress;

    private PictureAdapter mAdapter;
    private PicturePresenter mPresenter;
    private Context mContext;

    /**
     * 构造函数。
     */
    public BasePageView(Context context){
        this(context, null);
    };

    public BasePageView(Context context, AttributeSet attributeSet) {
        this(context, attributeSet, 0);
    }

    public BasePageView(Context context, AttributeSet attributeSet, int defStyleAttr){
        super(context, attributeSet, defStyleAttr);
        init(context);
    }

    /**
     * 初始化
     */
    public void init(Context context) {
        mContext = context;
        inflate(mContext, R.layout.base_view_layout, this);
        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        mProgress = (ProgressBar) findViewById(R.id.progress_bar);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
    }

    @Override
    public void showProgressBar() {
        mProgress.setVisibility(View.VISIBLE);
        mRecyclerView.setVisibility(View.INVISIBLE);
    }

    @Override
    public void hideProgressBar() {
        mProgress.setVisibility(View.INVISIBLE);
        mRecyclerView.setVisibility(View.VISIBLE);
    }

    @Override
    public void showMsg(String msg) {
        Toast.makeText(mContext, msg, Toast.LENGTH_LONG).show();
    }

    @Override
    public void showPictures(List<Picture> pictures) {
        mAdapter = new PictureAdapter(pictures);
        mAdapter.setRecyclerItemClickListener(new OnRecyclerItemClickListener() {
            @Override
            public void onItemClick(int pos) {
                mPresenter.onItemClick(pos);
            }
        });
        mRecyclerView.setAdapter(mAdapter);
    }

}

这样当需要对视图进行更改的时候,只需要更改这个类就可以了,不用在跑到 fragment 中去了。

于此同时,fragment 也瘦身成功了:

public class PictureFragment extends Fragment{

    private PicturePresenter mPresenter;

    public static PictureFragment newInstance() {
        return new PictureFragment();
    }

    public PictureFragment() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_picture, container, false);
       BasePageView basePageView = (BasePageView) view.findViewById(R.id.baseView);
        mPresenter = new PicturePresenterImpl(basePageView);
        return view;
    }

    @Override
    public void onResume() {
        super.onResume();
        mPresenter.onResume();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mPresenter.onDestroy();
    }

}

  改造之后,是不是更好理解了啊。当我们需要对某一部分需要修改的时候,能够轻松定位要修改的地方。

  好了,通过改造之后,相信大家对 MVP 的理解也就更加深刻了。

  希望这篇文章对大家有所帮助。

原文地址:https://www.cnblogs.com/huansky/p/8545734.html

时间: 2024-10-27 12:41:26

结合实例分析Android MVP的实现的相关文章

Android漫游记(5)---ARM GCC 内联汇编烹饪书(附实例分析)

原文链接(点击打开链接) 关于本文档 GNU C编译器针对ARM RISC处理器,提供了内联汇编支持.利用这一非常酷炫的特性,我们可以用来优化软件代码中的关键部分,或者可以使用针对特定处理的汇编处理指令. 本文假定,你已经熟悉ARM汇编语言.本文不是一篇ARM汇编教程,也不是C语言教程. GCC汇编声明 让我们从一个简单的例子开始.下面的一条ARM汇编指令,你可以添加到C源码中. /* NOP example-空操作 */ asm("mov r0,r0"); 上面的指令,讲r0寄存器的

Android Touch事件原理加实例分析

Android中有各种各样的事件,以响应用户的操作.这些事件可以分为按键事件和触屏事件.而Touch事件是触屏事件的基础事件,在进行Android开发时经常会用到,所以非常有必要深入理解它的原理机制. Android Touch事件原理描述 一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE...->ACTION_MOVE->ACTION_UP. 当屏幕中包含一个ViewGr

Android应用测试工具ThreadingTest查错实例分析

1      ThreadingTest产品简介 ZOA公司研发的ThreadingTest智能型测试工具系列一期,是基于程序源代码的白盒测试工具.采取前端分析器和后端结果分析分离的技术路线,实现对多种语言的编译器级分析和多维度测试. ThreadingTest的核心思想来源于非线性复杂软件工程体系.通过ThreadingTest基于测试用例集与动态代码覆盖的双向追溯的专利技术,使得对于大型应用系统的维护和修改变得不再盲目和极易出错,使得对大型软件的系统测试期和维护期的测试过程从无量化依据到有明

Retrofit源码分析以及MVP框架封装使用

阅读此文前请先阅读Retrofit+okhttp网络框架介绍 从上文中我们已经了解通过如下代码即可得到返回给我们call 以及 response对象,今天我们通过源码来分析这个过程是如何实现的. /** * 获取天气数据 * @param cityname * @param key * @return */ @GET("/weather/index") Call<WeatherData> getWeatherData(@Query("format") S

在Eclipse中使用MAT分析Android程序内存使用状况(转)

对于Android这种手持设备来说,通常不会带有太大的内存,而且一般用户都是长时间不重启手机,所以编写程序的时候必须要非常小心的使用内存,尽量避免有内存泄露的问题出现.通常分析程序中潜在内存泄露的问题是一件很有难度的工作,一般都是由团队中的资深工程师负责,而且随着程序代码量的提高,难度还会逐步加大. 今天要介绍一个在Eclipse中使用的内存分析工具——MAT(Eclipse Memory Analyzer,主页在http://www.eclipse.org/mat/).它是一个功能非常丰富的J

从Handler+Message+Looper源代码带你分析Android系统的消息处理机制

PS一句:不得不说CSDN同步做的非常烂.还得我花了近1个小时恢复这篇博客. 引言 [转载请注明出处:http://blog.csdn.net/feiduclear_up CSDN 废墟的树] 作为Android开发人员,相信非常多人都使用过Android的Handler类来处理异步任务. 那么Handler类是怎么构成一个异步任务处理机制的呢?这篇 博客带你从源代码分析Android的消息循环处理机制.便于深入的理解. 这里不得不从"一个Bug引发的思考"開始研究Android的消息

从Handler+Message+Looper源码带你分析Android系统的消息处理机制

引言 [转载请注明出处:从Handler+Message+Looper源码带你分析Android系统的消息处理机制 CSDN 废墟的树] 作为Android开发者,相信很多人都使用过Android的Handler类来处理异步任务.那么Handler类是怎么构成一个异步任务处理机制的呢?这篇 博客带你从源码分析Android的消息循环处理机制,便于深入的理解. 这里不得不从"一个Bug引发的思考"开始研究Android的消息循环处理机制.说来话长,在某一次的项目中,原本打算开启一个工作线

深入理解Android(5)——从MediaScanner分析Android中的JNI

前面几篇介绍了Android中的JNI和基本用法,这一篇我们通过分析Android源代码中的JNI实例,来对JNI部分做一个总结. 一.通向两个不同世界的桥梁 在前面我们说过,JNI就像一个桥梁,将Java和Native世界紧密的联系在了一起,在Android平台上如果没有Native层的支持我们的系统寸步难行,甚至Java中的虚拟机也是通过Native实现的. 二.MediaScanner类的简单介绍 MediaScannerr完成android中的多媒体文件的扫描工作.例如,mediasca

事件拦截机制分析(Android群英传)

内容是博主照着书敲出来的,博主码字挺辛苦的,转载请注明出处,后序内容陆续会码出. 当Android系统捕获到用户的各种输入事件后,如何准确地传递给真正需要这个事件的控件呢?Android给我们提供了一整套完善的事件传递.处理机制,来帮助开发者完成准确的事件分配与处理. 要了解触摸事件的拦截机制,首先要了解什么是触摸事件?顾名思义,触摸事件就是捕获触摸屏幕后产生的事件.当点击一个按钮时,通常就会产生两个或者三个事件--按钮按下,这是事件一:如果不小心滑动一点,这就是事件二:当手抬起,这是事件三.A