架构探险——Android MVP模式浅析

来源:互联网 发布:口技box速成软件 编辑:程序博客网 时间:2024/06/08 06:39

“善苑国尝贡一蟹,长九尺,有百足四螯,因名百足蟹。煮其壳胜于黄胶,亦谓之螯胶,胜凤喙之胶也。”——东汉郭宪撰的《汉武洞冥记》· 卷三

开篇简介:探险系列,是我对当前的一些Android项目架构,或框架的一些分析与探索。Android到目前为止还未有像Java Web一样有成熟的一套框架,无数的开发者都在暗夜中摸索着。也希望借本篇让读者做一个勇于吃螃蟹的人,有天亦能成为第一个吃螃蟹的人。


Android经典架构——MVP

之所以说它的经典,是因为在此之前,Android还处于一个混沌的阶段,在一些公司里,所有的操作都写在一个Activity或Fragment里,代码冗余,耦合度极高。打开一些关键页面,代码洋洋洒洒4000+。好一首长恨歌!对后面新进员工而言,维护成本太高了。

MVP的出现,不亚于盘古开天辟地般的举措。终于有一个层级分级明显、高度可复用、耦合度低的架构模式出现。于我的震撼不亚于当初学习Servlet的时候发现SSH这种整合框架的程度,当然后面也有Google自己推荐的MVVM(Data Binding )。这是后话,本系列之后也会为大家讲解。

介绍MVP前也应该给大家普及一下概念。关于概念,我想就是你的明白原理之后的对其的总结。这个我在巨人系列说过了,不撮我的痛点了…..

概念:MVP是经典架构MVC的继承与延伸。它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示展示。二者也有也有不同的地方:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller。

下图可以显而易见的看出来
MVP与MVC对比图

在传统的Java Web中,MVC再适合不过了,Controller只是负责什么样的View需要什么样的数据,然后去Model里找,最后让Model自己与View层交互而已,实现了显示与逻辑分开

而在Android中,我们可以勉强理解为它已经是一个MVC模式了。注意只是从某个角度来看,因为从别的角度出发,它并不是。

层级 描述 视图层(View) 一般采用XML文件进行界面的描述,使用的时候可以非常方便的引入。类似HTML的结构 控制层(Controller (一部分的View)) Android的控制层的重任通常落在了众多的Acitvity的肩上,不过其实也肩负着一大部分对View的操作,因此不能算是严格的MVC 模型层(Model) 对数据库的操作、对网络等的操作都应该在Model里面处理。


Android 设计之初,愿景是美好的。不过其实作为移动端,本质上与前端没什么区别。最多的还是Modle对View的动态控制。当然同时还有View的一些监听事件的响应。

同时,Java从来也不是一门简洁轻便的语言,即使加入了lambda。而且还有findViewById这种东西在。一个稍微复杂的页面仅仅初始化View控件就要好多行代码。这也是类似Butterknife这类框架存在的原因。

于是我们在一个Activity或者Fragment上一般要做如下几件事:
1. 初始化View控件
2. 维护控件View和Modle层的逻辑
3. 处理数据层,如果你没有将之独立出去处理的话。(其实也只是几个回调就能解决的。不过很多人还是喜欢写在Activity和Fragment中,不过有时候也是为了维护项目的完整性,不能就你搞特殊是吧。XD)
4. 其他逻辑,假如打点,传递数据,维护生命周期等。

可以看出,我们Activity与Fragment里做的事真的很多。项目维护的难易程度会随着项目周期而线性增长,这样维持下去,一个页面出现4000+的代码量不足为奇。

一个好的架构,是能将这种增长尽可能的降低。


因此,我们最需要的其实不是将Model层移出去(其实一样很需要,不过优先级可以低一点)。而是将我们的控制逻辑给移出Activity。将Activity从Controller层、部分View层的角色变成单纯View层。这样是不是会变得很逻辑明了,结构清晰?

MVP的引入由此而来。

试想一下,我们Activity只是一个View层,它只要知道什么情况下View要什么事。而什么时候要做,就不由Activity控制了。

概念说完了。大家可以细心比较下。二者的区别。接下来,我就用一个简单的demo来为大家展示一下,mvp的魅力。

那先休息一下吧~,先吸收下上面的知识点。

架构探险,不是说该如何使用某某架构,而是说出里面的思想,授人以渔。


以上说了我们的MVP,可不是The Most Value Player…..

现在我将讲诉如何搭建一个MVP工程架构。很多做Java Web的人看不起Android开发人员,首先,觉得android简单,其次,毫无架构可言,很多情况下,都不面向接口编程,只为了实现而编程

MVP的架构则是面向接口编程。如果你浸淫Java web的技巧,那MVP对你而言,不是什么复杂的架构。如果你只会Android,并如我一样是一个菜鸡,那就需要慢慢理顺,消化它,并自己写出自己的demo。

然后我先做一个东西瞧瞧看:
点击一个button,它会访问百度首页,然后show到我们的TextView上。
效果图如下(中间有showDialog,太快了。。。截不下来):
初始情况:
这里写图片描述
点击情况
这里写图片描述

看看我们所需要的材料:

原料 描述 StringView(接口) 定义着我们当前Activity(View)的行为 StringPresenter(类) 定义着Activity(View)与Presenter层交互的方法(比如传参)
调用Activity的方法
处理Model层返回数据 StringModel(类) 实现我们的数据请求 OnStringListener(只是数据返回的回调而已,用了事件总线。你完全可以不用它) 数据返回之后的回调,回调给Presenter层

(注意:这个只是最最简单的食材,最最普通的味道。最好的是将现有的类上再定义一个接口,定义一个规范。这样划分的粒度可以很容易的复用。听不明白没有关系,慢慢实践,你会越来越明白)


接下来直接看我们的Demo

没有太详细的注释,因为真的不难
没有太详细的注释,因为真的不难
没有太详细的注释,因为真的不难

重要的事情说三遍!!!

/** * Created by zhangyanye on 2015/8/23 * Description:获取String时,定义的行为。我们的Activity将会实现这个接口 */public interface StringView {    void showString(String result);    void showLoding();    void hideLoding();}


/** * 作为数据层的回调,被Presenter实现,可被事件总线框架取代。 */public interface OnStringListener {    /**     * 成功时回调     *     * @param result     */    void onSuccess(String result);    /**     * 失败时回调,简单处理,没做什么     */    void onError(VolleyError error);}


/** *  简单的用封装后的volley来请求网络,只要知道是请求了数据就好了。详细的可以看  看我的github,有我对Volley+gson+OkHttp的封装 */public class StringModleImpl {     /**     *  看到没有!!  传入了我们的回调listener     */    @Override    public void load(String url, final OnStringListener listener) {        /*数据层操作 */        VolleyRequest.RequestGet(Request.Method.GET, 0, url, "test",                new VolleyListener(FreeWalkerApplication.getApp(), new ResponseListener<String>() {                    @Override                    public void OnSuccess(String result) {                        listener.onSuccess(result);                    }                    @Override                    public void OnError(VolleyError error) {                        listener.onError(error);                    }                }));    }


/** * Created by zhangyanye on 2015/11/19 * Description:Presenter作为中间层,持有View和Model的引用,对model层的数据进行处理,控制View层的展示 */public class StringPresenter implements OnStringListener {    private StringView stringView;    private CommonModel commonModel;    public StringPresenter(StringView stringView) {        this.stringView = stringView;        commonModel = new StringModleImpl();    }    /**     *   对View层提供方法     */    public void setUrl(String url) {        stringView.showLoding();        commonModel.load(url, this);    }    //model 回调      @Override    public void onSuccess(String result) {        stringView.hideLoding();        stringView.showString(result);    }    //model 回调    @Override    public void onError(VolleyError error) {        stringView.hideLoding();        stringView.showString(error.getMessage());    }}


public class MainActivity extends Activity implements StringView {    private TextView textView;    private StringPresenter stringPresenter;    private Dialog dialog;    private Button button;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //初始化Persenter        stringPresenter = new StringPresenter(this);        initView();    }    private void initView() {        textView = (TextView) findViewById(R.id.txt);        dialog = new Dialog(this);        button = (Button) findViewById(R.id.btn);        button.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                stringPresenter.setUrl("http://wwww.baidu.com/");            }        });    }    @Override    public void showString(String result) {        textView.setText(result);    }    @Override    public void showLoding() {        dialog.show();    }    @Override    public void hideLoding() {        dialog.hide();    }}

嗯哈。代码就是这样了。简单的几个类,我就不画流程图了,看的不太明白,就跟着Button的OnClick事件走一遍,就能明白了

附上我的github的 源码地址。喜欢的话,记得给颗星星哈! 2333333333~


思考部分

1. 那问题来了!你丫的把这些东西抽取来了,那你想干嘛??好!那我和你说。

比如,我有三种请求。1. 收藏请求的;2. 刷新页面数据请求;3. 购买请求;
假如你粒度分的够细,将每一个部分都隔离出来。对应着相对MVP。等于是有多套(最多也就三套)这样的Class。

不好的地方时,你的类变得多了,不过包名取得好,不是什么问题。
好的地方是,你不用重复造轮子了,View层随着Activity或者Fragment会变。可是我们的处理逻辑是不变的。这样我们就可以不用重复造轮子了。

如果你分的不够细,一个页面对应的MVP处理着所有的请求。也能让代码可读性更高,容易维护

2.可能你会想,我当前的项目已经都成型了。4000+代码已经有了,这MVP对我有啥子用处。how to change it?go die?
其实,当你有一个class已经有4000+的代码的时候,说明你的项目已经一定的规模,尝试着分出module,对你的代码可维护性也有一定的提升。在独立的module中尝试使用MVP重构你的4000+class,特别针对业务相似的页面。我想会很有效的改善你的当前的困局。
顺口一提,我不提倡在业务相近的页面使用继承。尽管节约的代码量。可是,降低了代码的可扩张,维护性。


没有什么好与不好,一切只有适不适合。再美的女孩,你不喜欢。也是拉倒。


总结:我本以为有了官方data binding之后,MVP应该会冷了不少。没成想,MVP如今大行其道,很多人都在讨论。除了无需任何的框架,之外,也在于它高度的可扩展性。很多人在其之上进行了深度的改版。所以以它为开篇,我想再适合不过了~

就这样~希望能对大家有帮助!!

2 0
原创粉丝点击