博客文章改编自纽约Android开发人员Meetup由Okcupid赞助的谈话188bet金宝搏官网188博金宝电子体育频道(我们正在招聘!)

“瘦客户端?这里有三十年的瘦客户端......“,提示幽灵般的音乐。我会找到一种方法来与几乎任何情况开展笑话,考虑到这里使用的是无偿的。概念上说,这是真的。作为移动开发人员,我们经常将从布局从布局丢失到机器上的所有内容。虽然这种方法适用于许多情况,但有时候你会想要你的应用程序有点灵活。What I’m going to show you is a top-level walk-through of how I built a complex feature here at OkCupid and along the way demonstrate a few design patterns that you can employ in your own app to create remotely configurable layouts and behavior on-the-fly.

把哑巴放在薄 - 什么是瘦客户端?

任何其他名称的瘦客户端客户端就像愚蠢一样。薄客户端也称为Dumb客户端,通常是针对远程传输到服务器的计算机,这反过来将执行大部分显示和处理的工作。我在这里使用该术语来描述一个应用程序,它从服务器中采用它的视觉和行为提示,允许它直通地远程配置。

>瘦客户端应用程序只关注两件事。

在服用薄™方法来构建您的应用程序时,应用程序本身只关注几件事。显示数据并接收输入。关于第一个,我将向您向您展示将JSON融入意见的策略。至于第二,我们将介绍一个简单的方法来声明同一JSON中的行为,以便您可以向您的应用程序通知您应该在用户交互中进行的内容。

案例研究:发现

面对。滑动。面对。滑动。面对。滑动。- 刷新作为揭露用户的手段,促进比赛是一种常见的设计模式,几乎可以对任何一家在星期五晚上伸出的人再次发现自己的人来说,几乎可以理解,没有可预见的计划,超越狂欢的大英国烘焙秀的整个季节。几个月前,我们的产品团队聚集在一起,为用户互动,可以利用他们给予我们创造更有吸引力的体验的洞察力和数据的新方法。

结果是发现的,一页充满了用户可以与揭示巨大潜在匹配互动的模块。页面上的模块功能从Instagram照片中的所有内容都具有注释的问题和答案。关于发现的最伟大的事情?对于用户,纯粹的动态信息。关于发现的最糟糕的事情?对于开发人员来说,动态信息的纯粹量。但只有几个月的时间从想法带来生产,现在是时候走到黄铜钉,并弄清楚如何建立所有。

188bet金宝搏官网Okcupid Discovery - 与您策划的匹配。

切断额外的卡路里 - 处理数据。

在看发现一个人可能想要思考的第一件事时,请查看我们需要创建的大量商业模式。我们显示数据的动态页面,因此我们可以假设我们将把它全部扔进回收站。但是是什么?问题,用户,消息,matchInfo,兴趣,专辑。数据类AD ACSURDUM之后。让我们暂停。这是思维薄™发挥的地方。耦合您的业务逻辑,如此紧密,几乎肯定是不必要的,有时可以重构绝对的噩梦。当然你今天打电话给他们用户,但如果明天你的营销部门出现了一些其他疯狂的术语,那么像百吉卷?现在你必须返回并更改所有这些引用,即使您在屏幕上显示的内容有效地是完全相同的内容。

提示1:思考布局,而不是用户

这是一个有用的设计模式。而不是拥有一个以实体为中心的模型,为什么不具有基于业务不可知的布局的模型?模型描述页面上的物品,而不是在语义上。发现不需要关心这是艾琳,96%的匹配,这是她脸上的照片。

我们的视图仅关心需要填写的图片和两个文本字段。不是从服务器接收整个用户对象并在客户端上解析它,而是教导服务器仅向我们提供完全多的信息,因为我们需要在屏幕上显示。想想布局,而不是用户。


考虑到这一哲学,我们创建了一个为我们的团队工作的命名制度。我们的数据类的名称,而冗长,准确地描述所显示的内容。鉴于我们在我们的设计和工程团队之间概念共享的组件上构建了一个组件的目录,它很高兴地瞥一眼检查一个规格并确切知道我们需要使用哪些布局。像详细信息一样的名字只能带你到目前为止,所以我们完全远离他们。我们选择使用像top_text或bottom_border等位置属性的描述性分类命名我们的数据类和字段。使用任何适合您的工作。我们所有的布局模型都共享一个公共基类的layoutdata,您可以在文件的顶部定义,但我们会恢复到那时很快发生的事情。

我发现当有人花时间在现在做正确的事情时,他们是一个完美主义者,没有优先考虑的能力,而当有人花时间在过去做正确的事情时,他们就是大师的伟大前提。-xkcd.

该可重用的视图数据系统允许服务器将其想要的任何类型的信息放入容器中,并且应用程序本身只能为该信息发出导管,接收原始模型。然后,我们将这些模型注入到我们的视图中,并使用Android DataBinding将它们绑定到我们的视图中。188博金宝电子体育频道

如果我们应该决定明天,我们实际上希望使用该布局来显示广告或其他方式的信息 - 我们不需要APK更新来执行此操作。相反,我们只是在服务器上翻转交换机。听起来像魔法?该死的是。让我们简单地谈谈服务器工作的方式。

愚蠢的客户,智能服务器

构建一个薄的应用程序并不完全是魔法,它不会在某个阶段储存您或您的团队在某个阶段编写复杂的代码。这里的想法是,我们已经将大部分处理和业务逻辑移动到服务器,而不是我们通常在标准应用程序开发中看到。在使用多平台团队的真实产品上工作时,经常(读取:不可避免地)的重复逻辑意味着更多错误。多年前,我们介绍了延长的性别和方向的概念到Okcupid平台。188bet金宝搏官网现在,虽然一个完全值得和崇高的追求,所有这些选择的组合爆炸和他们的复杂匹配逻辑使代码成为所有类型的错误的培养皿。一年多,我们在我们的三个平台上玩了Whack-a-mole(android,iOS,桌面)插入错误。188博金宝电子体育频道最后,我们决定最好的事情是让服务器处理繁重的升降,而客户则处理显示。因此,愚蠢的客户口头禅出生:“不要在客户端做什么服务器可以做的事情。”

我不能在这里强调常识运动,而不是每件事都需要在服务器上处理 - 这不是1970年,我们没有在电线上发送点击到大型机。我们只是确保我们的大多数业务逻辑来自服务器本身,以便不需要重复写入。

提示2:作为爱语言的布局

建立一个坚实的API是技术功能的问题,因为它是关于UX的。作为消费者和开发人员坐下来创建可持续的终点,令人明确的要求和响应参数是必要的。但是,与所有代码一样,要求随时间变化。终端不如代码的其余代码的重构而少。没有两种关于它的方法,版本控制API很糟糕。

我们为发现端点创建了一个智能系统。客户端应用程序发送它可以支持的布局的枚举(例如PicturetitLesubtitle,avatarthreetext,toimageoneText) - 然后服务器通过布局进行优先级,并填写数据。

例如,如果您有一个旧版本,只能为PicturetitLesubtitle布局提供支持,我们将将您未来的日期的信息放在更简单的视图中。但是,如果您拥有我们的最新版本,我们可以设置服务器以优先考虑在支持的视觉上更丰富的布局中发送未来BAE的数据。这样,我们可以轻松支持来自不同版本的许多客户端,而无需强制用户升级和创建潜在损失。

提示3:多态性反序列化是超级()

现在我们有一个稳固的系统来告诉服务器我们可以支持哪种类型的布局,但是当它向我们发送那些满足布局的列表时会发生什么?Our Retrofit network call only knows that it’s requesting LayoutData (this is just what we call our layout data class, you can choose whatever makes your heart content), but it doesn’t know what specific subtypes and JSON doesn’t have a concept of classes and hierarchy, so how do we teach our app to parse the JSON into a list of the correct subtypes? Polymorphic type handling.

我们的layoutdata json的实际示例

在实践中,您将JSON对象上的字段指定为鉴别器,Deserializer将检查该字段并通​​过反射将整个对象解析为在那里指定的类。

使用名为RuntimeadaperFactory的类处理多态解串化。注册要将JSON的所有子类型注册到进入中,适配器将执行其余工作以向您提供数据的协调列表。Most deserializers have a concept of this, from GSON to Jackson (I believe there’s a PR out for support in Moshi as well, if not, it’d be trivial to code it up yourself. Open source ftw.) They all effectively work the same way.

提示4:如果您看不到它,数据并不意味着sh * t - 使用环氧树脂

我们来回发送数据,事情看起来很棒。那么我们如何在屏幕上得到它?从Airbnb上留下我的小粉碎,环氧树脂吧。环氧树脂基本上是类固醇的回收。您可以提供屏幕上想要显示的内容的环氧树脂,它将处理到达那里需要大量的繁重提升。对于发现,我们模拟了从我们的API响应中获得的LayoutData解释的状态,并将其馈送到EPOxyController,这将反过来差异,并使用任何更改更新回收站。

图为:在这个项目的三个月后,我有理智的唯一原因

在这种方式使用RecyclerViews时,即使对于相对静态数据,也有很多好处。Recyclerviews懒洋洋地加载视图层次结构,因此布局时间具有更快的速度,内存使用率较少,而新加载的数据不会重复无效视图层次结构。环氧树脂本身的使用相对简单,因此我不会在这里深入掩盖。主要的主旨是,我们向我们的数据绑定布局申报资源标识符并为我们生成模型,我们可以将Deage Degressy添加到处理RecyClerview的TypedexyController。

package-info.java.

在州:简要概要

这是我将在发现MVI架构的建筑块中带你去旅行的地方。在我们所做的大部分发展中,屏幕上发生的事情的概念状态仅在视图中保持。例如,我们正在加载一些数据并启动将在我们的数据操作完成之前运行的旋转器。该旋转器是否处于活动状态的状态仅在Spinner小部件本身中维护,而不是逻辑类中的字段,如演示者或ViewModel。因此,准确地重新创建像配置更改的内容,或坚持存储将是一项艰巨的任务。

在上一节中,我们看到了如何将“状态”传递给环氧树脂,并使用它来构建数据的视觉表示。那么州的意思是什么?这是一个经常具有各种定义的重载术语,但我正在推荐的是我们应用程序所有组件的当前状态的集中表示。这不是比这更幸福的。在Kotlin中,我们可以代表它,因为我们可以使用数据类来代表任何其他模型。这里的重要方面是,国家的财产保持不变。您的州类的属性也不应该没有定居者,并且属性本身应该反映您希望能够重新创建,持续或报告的任何数据。遵守此规则将使您能够具有不可预测的状态容器,这些容器不受其他操作的副作用。

对于发现,我们的州主要包括我们的布局数据的实例从我们的数据的反序列化派生。在状态中包含包含的属性,如CurrentPageIndex,可能不会来自JSON本身,而​​是仍然对应用程序当前状态的描述性。

如果我们的申请状态是一个不可变数据类(免责声明:Kotlin Vals没有定义不可改容,但我们不需要迂腐,对吧?),我们如何修改它,因为用户与我们的应用程序进行交互。我们已经完成了彻底的步行,解码了我们愚蠢的客户的视觉组件,因此让我们看看解码交互的哔哔声和垃圾沸腾。

定义和解码交互

让我们暂停怀疑并让自己暂时获得假设。如果有人要求您描述应用程序中可用的每一个可用的互动,您会知道在哪里看?我们大多数人都不会。现在说有一种方法可以轻松定义可以简洁且一致地在应用程序中采取的所有操作?SPOILER ALERT:有。双扰流器警报:我没有想到它。

FSA在左侧,右侧kotlin版本。

从JavaScript助焊剂标准操作规范中借用,我们可以使用密封数据类定义应用程序中的所有交互。这里的超级类是动作,我们的所有其他行动都来自它。然后,我们可以使用这些操作来告知状态响应用户交互,内部事件或API调用所需的方式。

您的行为应通过人类轻松读取和写入,简单,简单,直接的描述符,即发生或需要完成。它们可以是没有附加信息或数据类的对象,其中包含内部的相关信息。无论哪种方式,如果我们使用此模型,我们会看到我们最终得到了一个强烈的应用程序可以做的一切。换句话说,这些操作是抽象和编码行为的强大工具。让我们来看看我们如何使用它们来修改我们的州。

成为你想要在州看到的变化

所以通过我们的不太假设的行动,我们如何处理任何一个互动实例?当我们接受他人的行动时,我们做了同样的事情,我们做出了一些决定。我们需要通过控制流程构造来传递这些操作,该结构将解析它并决定如何处理该信息。

在像这样的单向数据流中,我们可以调用Consection Reducer,这是我从React World中的JavaScript Cousins借用的术语。还原器最容易使用全强kotlin建模建模什么时候功能。

减速器是纯功能,即返回的值(状态)仅由输入值(动作)决定,没有可观察的副作用。我们的代码的不同部分无法从Reducer实例之外任意更改我们的状态。Reducer寻找操作,然后使用包含的数据 - 内部创建完全新的状态。

一个常见的情绪通常会听到鹦鹉,是复制状态是昂贵且慢的操作。让我们花点时间暂停和务实地反映这一点。我们只复制了互动的状态 - 除非您正在使用数百个动作创建视频游戏,否则将简单归一化的形状复制了一种以正确归一化的形状复制了Sancey建立的状态。有关100多种型号的参考,复制和差异,以及超过100架的型号,大约9毫秒的位置。在60毫秒的jank限制下会导致您的UI中的任何明显延迟。要记住这样的数字是很重要的,所以我们没有冒险进行预优化的风险。另一方面,为您的应用状态具有单一的真理来源使开发和调试一个很容易的地狱。

所有这一切都很好,但实际上我们需要不时运行异步动作,最终会影响状态,因此让我们来看看用于处理这些的模式。

处理方面业务,创建行动

如果我们在应用程序中有相互作用或事件,则必须延长运行侧进程或异步操作,我们可以通过有时称为ActionCreator的东西。形式的ActionCreator与带有关键差分器的减速器非常相似,即它可以具有副作用但没有直接访问状态本身。

使用控制流程,我们可以使用rxjava可观察到的内容来解析操作,然后运行异步任务。例如,当侧面业务已完成时,例如在订阅Lambda中,我们可以调度另一个或另一个副作用诱导动作或将被减速器捕获并用于修改状态的最终动作。

所以现在我们完全了解如何描述应用程序的视觉表示和行为。记得发现?让我们回到那个,看看我们如何编码和实现这种行为。

最后步骤:解析和绑定操作

我们需要将动作从JSON进行反序列化,以便它们与运行该功能的主要有效负载以及远程可配置。使用与布局的相同模式我们使用多态解序列化将JSON操作解析为其子类型,以符合对象的JSON主体中的字段。

然后,我们必须将现在kotlin绑定到视图本身。为此,我们再次利用Android DataBinding的实力和灵活性。188博金宝电子体育频道操作本身被定义为视图上的字段,从同一JSON解析。在视图本身上,我们定义了将射击尚未享有的互动模式,即点击,长按,手势。一旦发生了用户视图交互,我们就向ViewModel发出信号,以通过RxJava主题激励动作。保存对状态,减速器和ActionCreator调度的父视图模型,该操作并相应地修改了该操作和状态。

这是一旦您的应用程序知道如何处理和转换任何给定的操作的内部状态,那么视图本身就是不可知论他们正在做的和显示。恭喜,你基本上只是让自己成为一个本机组件Web浏览器!无论哪种方式,我们现在都有一个愚蠢的客户机系统,我们可以远程配置打开的布局和行为。

一些决赛思想

希望每个人都有一些东西。其中一些概念非常复杂,很难在从如此高水平的情况下查看特征的同时进行正义,但如果有足够的兴趣,我将在后续帖子中更深入地重新审视这些方面。

该系统不是一个灵丹妙药,如所有自有的编程决策,都有权衡。但是我想澄清一件事是这样做:使您的应用程序以这种方式作为瘦客户端操作并不意味着它将无法脱机或缓存响应。Reducer仍应包含足够的逻辑以运行不需要访问Internet / Server的所有内部操作。在客户端上可以持久地持有其他任何内容,直到重新建立连接,就像您与厚客户端一样。

重要的是要记住,无具心地遵守任何给定的架构或设计模式,而是寻求创意解决方案并推动创新工程。请记住,架构只不过是您和您的开发人员之间的合同,所以做的工作(只记得记录它!)最后,任何人都可以复制代码,但你可以给Android社区的真实礼物是你自己的观点188博金宝电子体育频道。我很想听到你们所有人,所以随意关注并在推特上给我留言@brandonjf.

欢呼和快乐的编码!