Android架构设计---MVP模式第(一)篇之基本认实

版权声明:本文为LooperJing原创文章,转载请注明出处!

MVP 这种模式出现已经很久了,在网上有些关于 MVP 开源代码2014年就有了,近期有关注项目架构方面的内容,于是乎,作为一个还不懂什么是 MVP 的人,那么就一定要了解一下的。网上关于 MVP 的资料其实也不少,通常都要把 MVP 和 MVC 做一下比较,我喜欢直接了当,相信有耐心看MVP的人是一定懂 MVC 的,MVC 的略过。本文的项目地址是:https://github.com/herojing/JokeMVP,下面结合项目谈谈MVP是个什么东西,以下就当作自己的学习总结笔记吧。

一、什么是MVP?

MVP 是 Model、Presenter、View 的缩写,三个部分的关系如下图所示。


效果图

在 Android 项目中,负责界面展示的模块(所有的 Activitiy 、Fragment以及 View 的子类)都可以划分到 View 这个层次,所有的业务逻辑处理(请求网络数据、数据库读取等)可以划分到 Model 这个层次,为了使得 View 和 Model 之间松耦合,用 Presenter 帮助解耦。所以可以猜测,在具体实现中 Presenter 类肯定要持有 View 和 Model 的引用。现在来说一下,上图中三个箭头的意思。流程是这样子的,从左到右看,比如我们刚进入一个 Activity,那么这个 Activity 做为 View 层,肯定需要通知 Presenter 加载数据,而Presenter会继续调用Model层加载数据,等Model加载完毕后,回调给 Presenter,Presenter 持有View引用,再通知View更新界面。

二、MVP的效果

采用MVP的目的就是使得层次更加清晰,业务逻辑与 UI 分离,那么采用 MVP 以后的效果如何呢?DEMO 实现的是一个列表,效果如图下图所示,列表的内容是一些笑话信息。



如果上面的页面采用 MVP 的模式进行设计的话,那么Activity中的代码将非常清洁!请看下面。

public class MainActivity extends BaseActivity implements JokeView {

    // 不做分页加载的操作,所以这两个参数写死
    public static final String  PAGE_NUM           = "1";

    public static final String  PAGE_SIZE          = "20";

    private ListView            mListView;

    private JokePresenter       mJokePresenter     = null;

    private ArrayList<JokeInfo> mJokeInfoArrayList = null;

    private JokeAdapter         mJokeAdapter;

    @Override
    public void initVariables() {
        mJokeInfoArrayList = new ArrayList<>();
        mJokePresenter = new JokePresenterImpl(this);
    }

    @Override
    public void initView() {
        setContentView(R.layout.activity_main);
        mListView = (ListView) findViewById(R.id.main_page_joke_lv);
    }

    @Override
    public void loaderData() {
        mJokeAdapter = new JokeAdapter(this, mJokeInfoArrayList);
        mListView.setAdapter(mJokeAdapter);
        //通知 Presenter 加载数据
        mJokePresenter.getJoke(PAGE_NUM, PAGE_SIZE);
    }

    @Override
    public void showLoading() {
        // TODO 显示进度条
    }

    @Override
    public void hideLoading() {
        // TODO 隐藏进度条
    }

    @Override
    public void setJoke(Joke pJoke) {
        if (pJoke != null) {
            Joke.Result result = pJoke.getResult();
            if (result != null) {
                ArrayList<JokeInfo> jokeInfoArrayList = result.getJokeInfoArrayList();
                mJokeInfoArrayList.addAll(jokeInfoArrayList);
                mJokeAdapter.notifyDataSetChanged();
            }
        }
    }

    @Override
    public void showError() {
        TextView errorView = new TextView(this);
        errorView.setTextSize(20);
        errorView.setText("请求失败了");
        mListView.setEmptyView(errorView);
    }
}

我重新定义了一下 Activity的“生命周期”,这个 MainActivity 继承了 BaseActivity ,BaseActivity 的实现如下:

public abstract class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initVariables();
        initView();
        loaderData();
    }

    /**
     * 做初始化方面的工作,比如接收上一个界面的Intent
     */
    public abstract void initVariables();

    /**
     * 初始化控件
     */
    public abstract void initView();

    /**
     * 加载数据
     */
    public abstract void loaderData();

}

如果你觉的还不错,那么可以继续看下面了,下面将具体阐述 MVP 三个部分是如何协同操作的。

三、View层实现

在讲述View层实现之前,首先看一下,项目的整体结构划分,有个大致的感觉。如下图所示。感觉内容还是比较多的,但是不难,一步一步的看吧!

项目结构划分

如果要实现上面的效果,首先做一下需求分析,每一条的笑话实体类包括的属性有笑话内容、时间;所以建立一个 Joke 实体类是很简单的。View层承担着界面的更新,MVP 中一般将界面更新的职责都交给一个 XXView ,我们的项目姑且叫做 JokeView 。当 Model 层请求到数据的时候,通知 Presenter 层后,Presenter 层就会调用 JokeView 进行界面的更新,所以需要一个设置笑话的方法;请求会有加载时间,所以界面要显示 Loading ,请求结束后需要隐藏 Loading ;当断网等异常情况发生的时候,还需要提醒用户请求发生了错误,所以还需要显示错误界面的方法。综上,定义的 JokeView 接口如下;定义好 JokeView 后,就可以让 Activity 实现 JokeView 接口,重写里面的方法进行更新了。所以我觉得在 MVP 模式开发的过程中,最先确定的就是写一个 XXView。

public interface JokeView {

    void showLoading();

    void hideLoading();

    void setJoke(Joke pJoke);

    void showError();
}

四、Model 层实现

在 Model 层中做的主要的工作就是请求网络数据了。请求逻辑我用了 Volley ,具体可以看项目中是如何实现的,也是参考了网上一个开源代码,具体的地址记不清了 。

public class JokeModelImpl implements JokeModel {


    public static final String REQUEST_SERVER_URL="http://api.jisuapi.com/xiaohua/text?";

    public static final String APPKEY="&appkey=9814b57c706d0a23";

    //http://api.jisuapi.com/xiaohua/text?pagenum=10&pagesize=3&appkey=9814b57c706d0a23
    @Override
    public void getJoke(String pNum, String pSize, final OnJokeListener pOnJokeListener) {

        VolleyRequest.newInstance().newGsonRequest(REQUEST_SERVER_URL+"pagenum="+pNum+"&"+"pagesize="+pSize+"&sort=addtime"+APPKEY,
                Joke.class, new Response.Listener<Joke>() {
                    @Override
                    public void onResponse(Joke pJoke) {
                        if (pJoke != null) {
                            pOnJokeListener.onSuccess(pJoke);
                        } else {
                            pOnJokeListener.onError();
                        }
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        pOnJokeListener.onError();
                    }
                });
    }
}

其中 OnJokeListener 是 Presenter 层中定义的接口,用与通知 Presenter 层要调用 View 层更新数据。

public interface OnJokeListener {

    /**
     * 成功的时候回调
     * @param pJoke joke
     */
    void  onSuccess(Joke pJoke);

    /**
     * 失败的时候回调
     */
    void  onError();
}

五、Presenter 层实现

在 Model 层和 View 层都定义好了之后,就可以写 Presenter 层了,之前已经多次说过 Presenter 层作为 Model 和 View 的桥梁,需要持有 Model 和 View 的引用。Presenter 需要实现 OnJokeListener 接口,具体的实现如下:

public class JokePresenterImpl implements JokePresenter, OnJokeListener {

    // P层作为M层和V层的衔接者,需要持有JokeView和JokeModel的引用

    private JokeModel mJokeModel = new JokeModelImpl();

    private JokeView  mJokeView;

    public JokePresenterImpl(JokeView jokeView) {
        mJokeView = jokeView;
    }

    /**
     * 调用M层取数据,getJoke由所展示的界面(Activity)调用
     * 
     * @param pNum
     * @param pSize
     */
    @Override
    public void getJoke(String pNum, String pSize) {
        mJokeView.showLoading();
        mJokeModel.getJoke(pNum, pSize, this);

    }

 /**
     * 接收M层的回调,调用View 层进行界面的刷新
     *
     */
    @Override
    public void onSuccess(Joke pJoke) {
        mJokeView.setJoke(pJoke);
    }

    @Override
    public void onError() {
        mJokeView.showError();
    }
}

六、总结

最后重新梳理一下 MVP 的编写方式。
1、 根据项目需求,写一个 XXView 接口。然后让对应的 Activity/Fragment 实现这个接口。View 层基本搞定!
2、编写 Model 层,主要就是网络数据请求了或者其他什么耗时操作,实现方式尽情发挥你的想象,但是最后一定需要用 Presenter 层定义的接口,回调给 Presenter 通知 View 层 更新数据。
3、编写 Presenter 层,Presenter 层需要持有 View 层和 Model层的引用,并且实现 Presenter 层定义的回调接口。在回调接口中调用 View 层的代码 进行界面更新,最重要的是,有一个调用通过Model层的方法,在此方法中,调用 Model 层请求数据。
4、回到View 层的Activity ,调用 Presenter 层获取数据。到此完成。

备注:为了遵守面向接口编程的原则,做了一下接口的抽取。如Presenter 中 实现了 JokePresenter 接口,Model 层中实现了 JokeModel 接口。好了,如果在阅读中,发现了有错误的地方,还望指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容