Android UI:机智的远程动态更新策略

腾讯Bugly特约做者: 王金波json

问题描述

作过Android开发的人都遇到过这样的问题:随着需求的变化,某些入口界面一般会出现 UI的增长、减小、内容变化、以及跳转界面发生变化等问题。每次发生变化都要手动修改代码,而入口界面一般具备未读信息提醒这样的“小红点”逻辑;一旦UI变化,“小红点”逻辑也要从新计算。若是不一样的RD来维护这些代码,耦合性很是高,出错几率也很大。本文以自选股的我的页卡为例(界面以下图所示),并给出了一套方案来解决动态更新UI的问题以及更好的解决未读提醒的逻辑。数据结构


旧的方案(Phase out)

(1)对于UI动态变化的问题,一般结合远程控制来解决。以上图的“资产管理”为例,旧的解决方案会在XML写死所有的item,如:“港股交易”、“基金交易”和“精品理财”这三个item。而后根据后台传递过来的json解析出须要隐藏哪些item。点击不一样的item会跳转到不一样的activity(以下图所示),这部分跳转操做也是写死在代码中的。app

这解决了一部分问题,可是若是需求新增了item,好比新增了“沪深交易”、“美股交易”,那就须要改动现有代码了。异步

(2)对于未读指示(小红点)功能,它的做用是,有未读信息来了,须要在UI上面显示一个小红点提醒用户。好比下图的,股友动态的头像提醒,资产管理的“NEW”提醒,系统设置的新版本提醒等。函数

旧的方案是定义了一个int型(32位),用它的每一位表明一个UI上的Item。好比好友动态是第1位,未读提醒是第2位... 小红点思想是哪一个item有未读信息,则该int型对应的那一位就置1,不然为0。一旦某个item有未读提醒的改变,则将这个int型对应的位改变,异步写入SharedPreference中,同时利用观察者模式通知UI作更新,以下图所示:ui

上述作法整体来讲最大的缺陷就是没有作到“开放-封闭”原则。面对扩展的时候,即添加一个item则不得不修改现有代码,须要在该int型中添加一位标志位,观察者模式也要注册新item。因此下面我会介绍另外一种方案能够更好的解决该问题。spa


新的解决方法

(1) 数据抽象对象

首先进行数据的抽象,并将UI进行分组,以下图所示:递归

按照组合模式,将数据以树形结构组织起来,表现“总体/部分”层次结构,以下图所示。这样作的好处是,能够以一致的方式来处理个别对象以及对象组合。蓝色的表示节点,而绿色的表示叶节点。接口

组合模式的类图,以下所示:

对UI进行的数据抽象。不管是ListItem列表项,仍是GridView Item的项,都采用了PersonalItem对象来表示,以下所示:

对于PersonalItem来讲,有些没有意义的方法(如add()、remove()、getChild())就不实现,调用它们会抛出UnsupportedOperationException()异常。

(2) 完美解决未读提醒(小红点)的问题

关于计算小红点,PersonalGroup类利用组合+迭代器的模式,代码以下:

这里使用了迭代器,用它遍历全部PersonalComponent组件。遍历过程当中可能遇到PersonalItem也可能遇到PersonalGroup。因为它们都是PersonalComponent,且实现了getUnreadIndicator()方法,那咱们只需调用getUnreadIndicator()便可。

如上图所示,PersonalGroup中加载的是PersonalComponent,多是PersonalItem也多是PersonalGroup。组合模式的优势就是无视具体类型 -- 获取出来的都是PersonalComponent,而后利用多态,调用具体类的getUnredIndicatorCount()方法。若是是PersonalGroup,则继续调用它的这个方法(与此方法同样,会开始另外一个遍历);若是为PersonalItem,则说明遍历到了树形结构的末端(即叶节点),则进行以下处理:

若是getUnreadIndicator为true,则表示该PersonalComponent须要显示小红点。所以,利用上述组合+迭代方式,运用递归在根节点处进行一次调用便可。以下图所示,当计算出叶节点“A股大赛”有未读提醒,则它上级的groups也有未读提醒,一直统计到根节点。

getUnredIndicatorCount()是每个item本身来决定本身是否须要展现小红点的方法。这就是将局部与总体解耦了。总体上面,须要计算小红点,至于如何计算则委托给具体类来实现。即面向对象中的将 "作什么" 与 "怎么作"分开。RD能够从中解放出来,没必要关注总体实现,只需关注本身的实现便可。好比,须要在“资产管理”中添加“美股交易”,RD只需添加“美股交易”的内容便可。下一节会说明,这部份内容也由远程控制来代劳了,远程控制传递过来的Date与本地存储的Date比较,若是是新的Date值,则证实这个Item为“NEW”,则对应的小红点须要显示。

(3)远程控制动态更新UI

当远程控制发生变化时(5分钟主动发一次请求),经过解析远程控制接口返回的json串,生成PersonalItem对象的列表。其中每一项对应UI上面的一个Item。须要注意的是,这里还包含了一个URL,它是点击UI控件跳转的URL。以“资产管理”为例,它包含“沪深交易”、“基金交易”等子项。当点击任意一个子项的时候启动的是同一个Activity - WebviewActivity,它包含一个WebView控件。由于每一个子项的跳转URL不同,因此这个WebView load了不一样的URL,即完成了跳转不一样界面的问题。 而后按照上面描述的树形结构,把PersonalItem放到Groups中。以下图所示:

Model存储了待显示的数据结构。这份数据经过Parser的解析生成UI的内容。过程以下图所示:

Parser模块是一个递归函数,递归的对Model进行解析。并将解析出来的List Item、Grid Group、GridView Item加载各自的XML文件,在程序中动态的添加UI组件。其中onClick事件是在定义PersonalItem的时候已经写好了回调。例如,“资产管理”属于Grid Group,其子项“沪深交易”、“基金交易”等属于GridView Item。在上述“Build PersonalItem Objects”步骤中,已经定义了onClick方法,调用onClick方法跳转至WebViewActivity,这个Activity会加载不一样GridView Item的URL,从而实现点击不一样item跳转不一样页面的目的。

Note:

对于ListItem元素,即上图的列表项(不是GridView元素),并无实现远程更新的策略。由于它们跳转的逻辑是跳转到各自的Activity,是固定不变的;而且它们的文字描述、图标、是否隐藏均不须要后台来控制更新。故实际项目中,只对GridView内容做了远程控制动态更新UI机制的处理。

另外,在经过远程控制动态更新UI的过程当中也遇到了一些坑,好比远程控制更新的时刻,刚好用户退出app,此时系统恰好销毁activity。那么在执行到上述Parser模块的inflateUI的时候就须要判断当前上下文是否为空,若是为空则直接退出。


结论与数据

本文经过将UI数据进行抽象,利用组合模式进行数据的构建。利用递归的方式将数据映射为UI。同时处理了点击事件。数据源则能够经过远程控制动态的更新,RD从中解放。另外,组合+迭代器的方式完美的解决了小红点的问题,遵循了“开放-封闭”原则,将“作什么”与“怎么作”分开。下图从数据的角度描述了改版先后 代码量、Bug量 以及 RD工做量的差别。


腾讯Bugly简介

Bugly是腾讯内部产品质量监控平台的外发版本,其主要功能是App发布之后,对用户侧发生的Crash以及卡顿现象进行监控并上报,让开发同窗能够第一时间了解到App的质量状况,及时机型修改。目前腾讯内部全部的产品,均在使用其进行线上产品的崩溃监控。

相关文章
相关标签/搜索