页面结构化在 Android 上的尝试

导语:MVP开发模式可以帮助项目结构解耦,但其庞大的方法数增加,较为笨重设计对于手Q项目并不很适合。参考之前Web开发经验,提出以页面结构化的解耦方式组织代码。下面讲讲Lego在Android上一次小小尝试

一,MVP简介

MVC太过常见这里不啰嗦。实际应用MVC当中,Activity占据打部分的工作,View和Controller的身份分不清。而MVP则是一种设计模式专门优化Activity / Fragment。

先来看看MVP模式的核心思想:View不直接与Model交互

MVP 把 Activity 中的 UI 逻辑抽象成 View 接口,把业务逻辑抽象成 Presenter 接口,Model 类还是原来的 Model

在MVP设计模式中,

View:由Activity充当,并且响应生命周期

Model:还是原来的数据层,网络,缓存,解析等。

Presenter:作为View和Model的中间纽带,View不能直接对Model进行操作,必须经过Presenter

View interface:需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合

二,日迹MVP实战应用

【Mode层】我们直接忽略

【View Interface】首页的View接口,抽离出view和presnter交互的接口。由Activity继承实现(Now.java,QQStoryMainActivity.java)

public interface IMyStoryListView {

    public void setData(MyStorys myStoryList, RecentStory recentStoryList);

    public void setSegmentData(String key, Object data,boolean needRefreshUi);

    /**

    * 更新数据后刷新界面走的回调

    * @param success

    * @param isManualPullRefresh

    */

    public void pullRefreshCompleted(boolean success,boolean isReqCompleted);

    public void launchNewVideoTakeActivity(boolean autoStart, boolean checkSo, int entranceType,String extra);

    public void setPlayVideoBtnDisplay(boolean display);

    public void showStartDownload();

    public void showDownloadCompleted(boolean success);

    public void storyPreLoadCompleted(String category, String uin);

    public void LoadMoreCompleted(boolean repositoryUpdated, boolean isEnd);

    public void showEmptyView(boolean display);

    public void requestDataCompleted();

    public void openMyStoryListView(boolean open);

}

【View】我们的Activity实现了View接口,并且实现生命周期

public class QQStoryMainAcitivty extends QQStoryBaseActivity implements IMyStoryListView {

    protected StoryHomePushYellowBarHandler mStoryHomePushYellowBarHandler = new StoryHomePushYellowBarHandler();

    protected MystoryListView mainListView;

    protected IMyStroyPresenter myStoryListPresenter;

    @Override

    protected boolean doOnCreate(Bundle savedInstanceState) {

        super.doOnCreate(savedInstanceState);

        mainListView = (MystoryListView) super.findViewById(R.id.qqstory_story_main_listview);

        //Presenter

        myStoryListPresenter = new StoryListPresenter(this);

        myStoryListPresenter.setIView(this);

        return true;

    }

    @Override

    public void onStartAutoRequestFromNet() {

        startTitleProgress();

        mainListView.pullToRefresh();

        mStoryHomePushYellowBarHandler.clearYellowBar();

        myStoryListPresenter.requestAllDataFromNet();

    }

    private void startTitleProgress(){

        // do more

    }

}

举个例子,用户下拉刷新一下。触发到Activity的onStartAutoRequestFromeNet。View逻辑在Activity。

业务逻辑则由Presnter的requestAllDataFromNet去实现。

【Presenter】具体的View->Model,Mode->View由这里实现,其中View是有View接口抽象,进一步规范化View的逻辑。

必要是可以抽出Presenter接口(其实日迹这里没有必要)

public class StoryListPresnter implements IMyStroyPresenter{

    protected IMyStoryListView mIView;

    protected FeedItem mFeedItem;

    protected ParallelStepExecutor mRequestNetDataExecutor;

    @Override

    public void onCreate(boolean needUpdateFromNet) {

        // 生命周期的逻辑处理

        mFeedItem = new FeedItem();

    }

    @Override

    public void setIView(IMyStoryListView IView) {

        // 设置View接口(目前实现的是Activity,但其实由其他Fragment,View实现都是可以的,这就是MVP的好处之一,解耦)

        mIView = IView;

    }

    public boolean requestAllDataFromNet() {

        mRequestNetDataExecutor.addStep(new GetUserSelfInfoStep(null))

                .addStep(new ReportWatchVideoListStep(StoryListPresenter.this))

                .addStep(new GetUserGuideInfoStep(StoryListPresenter.this))

                .onCompleted(new SimpleStepExector.CompletedHandler() {

                    @Override

                    public void done(FeedItem item) {

                        // 伪代码

                        mFeedItem = item;                      // 处理Model层

                        mIView.openMyStoryListView(mFeedItem);  // 根据View接口调用View更新

                    }

                }).run();

    }

}

MVP的优缺点:

优点:

解耦,绝对的。不然抽这么多接口干嘛

模块职责明确,层次清晰

Presenter可复用(在日迹的需求中,首页和4Tab公用一个Presnter)

方便单元测试

避免Activity内存泄露, Acitvity一身轻松

MVP的缺点也是非常明确的:

非常的笨重。一个View就对应一个Presenter,轻业务一个Activity能解决的就不要解决

Presnter依然逻辑繁重。Acitivty轻松了,业务逻辑庞大的时候Presnter依然是大胖子。

代码复杂度,学习成本。这玩意不好理解,需要实战中理解。

在手Q项目里,MVP会激增很多方法数。

三,Lego页面结构化

前面铺垫这么多,终于到我要吹水的时候了。MVC,MVP,还有MVVM等MVX系列的设计模式,都是一种大而全的统一管理。在项目结构中最为关键其实是:分模块!

看看某宝的首页,顶部搜索栏,banner,导航分类,抢购,特价,底部Tab。这是一个Activity的话,你再怎么MVP,也是需要划分模块,然后分而治之。

一个再大的系统,都可以划分一个个小的模块,分而治之

页面结构化,并不是新玩意,是当时做web的一套代码风格。下图是当时做Web总结组件化的一张图。现在看来,也就并没有过时

页面被划分问一个个区域的模块,有自身的逻辑和规划。有人说,这不就是一个个组件嘛。然后“页面结构化”并不是指组件。

例如上图的tabContainer,imgsContainer,listContainer,每一个模块都有自己的渲染模板(xml),请求的数据的CGI(数据源),自身的事件绑定(listener) ,状态机(生命周期),并不只是一个组件,而是一个个有自己生命力,能自己管理的小页面。

根据页面结构,划分出一个个独立维护模块,这就是页面结构化。

页面结构化(Lego)与组件化的区别

组件处于通用性,是不带业务逻辑的。而页面结构化是带业务逻辑。

页面结构化目的是为了代码维护性,项目管理,优化。组件复用可以有,但不是必要

组件与Lego不冲突。组件 +数据,业务逻辑 = Lego

下面就以问答的形式,用日迹评论赞项目实战,来讲解Lego好处

四,分析页面结构化特性

Lego自己拉取自己的数据,如果一个页面5,6个模块,就拉5,6分PB协议,谈何性能?

这里带出Lego两个特性:

每个Lego是有自己的数据,并不是一定要自己拉取,数据可以有其他Lego传递

Lego有父子关系。一个页面/Activity需要一个顶层Lego管理

日迹首页评论赞

public FeedCommentLikeLego(Context context, Activity activity, ViewGroup parentView, HomeFeedItem feedItem, int feedType) {

    super(context, parentView);

    mHomeFeedItem = feedItem;

    mFeedItem = feedItem.mFeedBasicItem;

    mActivity = activity;

    mFeedType = feedType;

    mLikeManager = (LikeManager) SuperManager.getAppManager(SuperManager.LIKE_MANAGER);

    mParentView = LayoutInflater.from(context).inflate(R.layout.qqstory_feed_commentlike_view, parentView, true);

    // 页面结构

    FeedCommentLego commentLego = new FeedCommentLego(mContext, mParentView, mFeedItem, mFeedType);

    FeedLikeLego likeLego = FeedLikeLego.createIndexFeedLikeLego(mContext, activity, mParentView, mFeedItem, mFeedType);

    addLego(LEGO_KEY_COMMENT, commentLego);

    addLego(LEGO_KEY_LIKE, likeLego);

    commentLego.feed(mHomeFeedItem.getCommentList());

    likeLego.feed(mHomeFeedItem.getLikeEntryList());

    boot();

}

从FeedCommentLikeLego的构造方法,我们得知

我是爸爸,我有两个儿子

我两个儿子不争气,需要我来喂养数据,自己不会挣钱(自己不拉数据)

全家我是一家之主,启动我说了算(Lego启动boot后,会自己拉数据自己渲染,同时子Lego也会相继boot)

日迹710这里就有场景,体验出Lego切换数据源的优势。

【首页】出于性能优化,都会做请求合并。返回多个Feed的视频列表,评论赞列表数据。

commentLego.feed(mHomeFeedItem.getCommentList());

likeLego.feed(mHomeFeedItem.getLikeEntryList());

被喂养数据后,Lego内部的DataProvider将不启动

【详情页】同一Lego,默认情况就会启动资金的DataProvider,会自己拉数据

@Override

public LegoDataProvider getDataProvider() {

    return new FeedLikeDataProvider(this, mIsDetailPage);

}

一个Lego类是究竟是什么?Lego类之间的纽带?

大部分页面的渲染流程线,如下图

我们把这些常用的网络请求,处理数据,事件绑定,上报,容错处理等一系列逻辑方法,以页面块为单位封装成一个Lego模块。

这样的一个抽象层Lego,我们可以清晰地看到该页面块,请求的数据是什么,绑定了什么事件,做了什么上报,出错怎么处理。

最后加上生命周期,页面结构化的Lego,已经算是一个完整的功能单元了。

继承LegoBase,有几个核心的方法需要重写:

还有生命周期方法可以重写,但不是必要的。

你阅读/接手一个Lego类,会是件很轻松的事情。一个Lego类,核心方法这几个,其余都是业务逻辑方法。

改事件去该Lego的EventHandler,数据要改去DataProvider,产品要求大V才展示底部尾巴,好,去render方法找。

Lego之间的纽带,有三个:

parentView(公用xml)

feedData(公用数据)

getLego(Lego关系)

四,总结

Lego的核心思想是:页面结构分模块,分而治之。解耦,代码可读性高,底层统一优化。

在使用了两个版本之后,感觉完成度还是不够。

顶层Lego情况复杂,底层统一优化不好做

接口之间约束,不够自由

但是对比MVP,Lego能体验出轻便,逻辑清晰,方法数量少的优势。

Lego页面结构化的应用其实还在尝试阶段。以上算我的一些个人思考和总结。

更多MelonTeam团队文章,请点击下方“阅读原文”

如果您觉得我们的内容还不错,就请转发到朋友圈,和小伙伴一起分享吧~

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

推荐阅读更多精彩内容