页面结构化在 Android 上的尝试
本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/M45DM5Ix7a2fmrsE8VPvxg
作者:bizaitan
导语: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页面结构化的应用其实还在尝试阶段。以上算我的一些个人思考和总结。
更多精彩内容欢迎关注腾讯 Bugly的微信公众账号:
腾讯 Bugly是一款专为移动开发者打造的质量监控工具,帮助开发者快速,便捷的定位线上应用崩溃的情况以及解决方案。智能合并功能帮助开发同学把每天上报的数千条 Crash 根据根因合并分类,每日日报会列出影响用户数最多的崩溃,精准定位功能帮助开发同学定位到出问题的代码行,实时上报可以在发布后快速的了解应用的质量情况,适配最新的 iOS, Android 官方操作系统,鹅厂的工程师都在使用,快来加入我们吧!
页面结构化在 Android 上的尝试的更多相关文章
- 【转】移动Web单页应用开发实践——页面结构化
1. 前言 在开发面向现代智能手机的移动Web应用的时候,无法避免一个事实,就是需要开发单页应用(Single Page WebApp).对于不同的系统需求,单页应用的粒度会不同,可能是整个系统都使用 ...
- 移动Web单页应用开发实践——页面结构化
1. 前言 在开发面向现代智能手机的移动Web应用的时候,无法避免一个事实,就是需要开发单页应用(Single Page WebApp).对于不同的系统需求,单页应用的粒度会不同,可能是整个系统都使用 ...
- HTML结构化
目的:为开发页面时有一套明确的页面结构化实施方案,提高开发效率: HTML结构化指的其实就是使用HTML语义化标签根据web标准书写具有明确结构逻辑的HTML代码的一种思路: 说白了重点就是:页面实际 ...
- [Apache]网站页面静态化与Apache调优(图)
---------------------------------------------------------------------------------------------------- ...
- 只能用于文本与图像数据?No!看TabTransformer对结构化业务数据精准建模
作者:韩信子@ShowMeAI 深度学习实战系列:https://www.showmeai.tech/tutorials/42 TensorFlow 实战系列:https://www.showmeai ...
- 你真的了解字典(Dictionary)吗? C# Memory Cache 踩坑记录 .net 泛型 结构化CSS设计思维 WinForm POST上传与后台接收 高效实用的.NET开源项目 .net 笔试面试总结(3) .net 笔试面试总结(2) 依赖注入 C# RSA 加密 C#与Java AES 加密解密
你真的了解字典(Dictionary)吗? 从一道亲身经历的面试题说起 半年前,我参加我现在所在公司的面试,面试官给了一道题,说有一个Y形的链表,知道起始节点,找出交叉节点.为了便于描述,我把上面 ...
- 页面静态化2 --- 使用PHP缓存机制来完成页面静态化(上)(ob_flush和flush函数区别用法)
我们可以使用PHP自带的缓存机制来完成页面静态化,但在这里,需要说明一点,仅靠PHP缓存机制并不能完美的解决页面静态化,往往需要和其他页面静态技术(通常是伪静态技术)结合使用 例子: 当访问一个页面时 ...
- 基于easyui开发Web版Activiti流程定制器详解(三)——页面结构(上)
上一篇介绍了定制器相关的文件,这篇我们来看看整个定制器的界面部分,了解了页面结构有助于更好的理解定制器的实现,那么现在开始吧! 首先,我们来看看整体的结构: 整体结构比较简单,主要包括三个部分: 1. ...
- XHTML 结构化:使用 XHTML 重构网站
http://www.w3school.com.cn/xhtml/xhtml_structural_01.asp 我们曾经为本节撰写的标题是:"XHTML : 简单的规则,容易的方针.&qu ...
随机推荐
- Go笔记之一:工程项目结构的注意事项
Go笔记之一:工程项目结构的注意事项 对 Go 项目目录的理解 (Windows平台为例) 刚安装完的 Go 需要设定环境变量,最关键的环境变量有三个,GOROOT.GOPATH和GOBIN.GORO ...
- SpringMVC接口测试异常:Can not deserialize instance of int out of START_OBJECT token
之前使用springmvc搭建了restful风格的接口服务,在使用mockmvc进行集成测试的时候出现了异常:Can not deserialize instance of int out of S ...
- spring security oauth2 jwt 认证和资源分离的配置文件(java类配置版)
最近再学习spring security oauth2.下载了官方的例子sparklr2和tonr2进行学习.但是例子里包含的东西太多,不知道最简单最主要的配置有哪些.所以决定自己尝试搭建简单版本的例 ...
- Spring Boot中使用Actuator的/info端点输出Git版本信息
对于Spring Boot的Actuator模块相信大家已经不陌生了,尤其对于其中的/health./metrics等强大端点已经不陌生(如您还不了解Actuator模块,建议先阅读<Sprin ...
- 2101: Bake Off
Description Davy decided to start a weekend market stall where he sells his famous cakes. For the fi ...
- CSS3实现轴心为x轴的3D数字圆环
当做混合开发时,总有各种意想不到的酷炫效果的需求等着你.不过这个还好,先备着方便以后用. 先上效果图: 总结一下:此效果的完成基于以下几个关键点: 1.DOM结构,为每个DIV设置旋转后,一次也会影响 ...
- BZOJ_3207_花神的嘲讽计划Ⅰ_哈希+主席树
BZOJ_3207_花神的嘲讽计划Ⅰ_哈希+主席树 Description 背景 花神是神,一大癖好就是嘲讽大J,举例如下: “哎你傻不傻的![hqz:大笨J]” “这道题又被J屎过了!!” “J这程 ...
- BZOJ_3223: Tyvj 1729 文艺平衡树 _splay
题意: 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 分析: ...
- Django中Middleware中间件
Django中Middleware中间件 1 Middleware中间件概述 django中间middleware实质就是一个类,django会根据自己的规则在合适的时机执行中间件相应的方法.实际上当 ...
- Ubuntu18.04美化主题(mac主题)
前端时间Ubuntu18.04LTS发布,碰巧之前用的Ubuntu16.04出了一点问题,懒得解决,索性就换了Ubuntu18.04. 成果: 参考博客:https://www.cnblogs.com ...