博客文章改编自我在纽约的Android开发者聚会赞助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.

把薄的阿呆 - 什么是瘦客户机?

任何其他名称瘦客户机的客户将是一样愚蠢。薄的客户端,也被称为哑客户端,通常是用于在远程到服务器,这反过来会做最显示和处理工作的最优化的计算机。我用这个词在这里描述的应用程序,需要它的视觉和行为线索从服务器允许它在远程的即时进行配置。

>瘦客户机应用程序只关心两件事情。

当服用薄™方式建立你的应用程序,该应用程序本身只关注几件事情。显示数据和接收输入。关于第一,我会告诉你的反序列化JSON到美景的策略。至于第二个,我们将介绍一个简单的方法来在同一JSON声明的行为,这样可以让您的应用程序是什么应该像点击的用户互动做的事情。

案例分析:发现

面对。刷卡。面对。刷卡。面对。刷卡。- 滑动操作的暴露用户和促进比赛的手段是一种常见的设计模式,几乎立刻理解的谁曾经发现自己再次独自在周五晚上Tindering与超越狂欢无法预见未来计划的人看大不列颠烘焙展会的一整个赛季。几个月前,我们的产品团队聚集在一起,设想用户交互,一个可以利用他们已经给我们创造了更多互动交流的洞察力和数据的新方式。

其结果是发现,一整页的模块,用户可以与发掘潜力巨大的比赛互动。页面功能,一切从Instagram的照片,注释的问题和答案模块。对发现的最伟大的事情?对于用户来说,动态information.The最糟糕的事情有关发现巨量?对于开发人员,动态信息的绝对数量。但只有一两个月才能利用此功能从概念到生产,是时候静下心来,黄铜钉子,并找出如何设计这一切。

188bet金宝搏官网OkCupid发现 - 匹配策划为您服务。

切出多余的卡路里 - 与数据处理。

当发现看的第一件事,人们可能会认为是看商业模式的数量庞大,我们需要创建。我们的数据显示一个动态的页面,所以我们可以假设,我们将投掷它全部变成RecyclerView。但是,什么是呢?问题,用户,信息,MatchInfo,兴趣,专辑。数据类归谬法后的数据类。让我们暂停。这是思维瘦™的用武之地。与显示器连接你的业务逻辑如此紧密,几乎肯定是不必要的,有时可以重构一个绝对的噩梦。当然,你今天的用户打电话给他们,但是如果明天你的营销部门想出了其他的一些疯狂的名词,就像百吉饼?现在你得回去和改变,即使你正在展示在屏幕上实际上是完全相同的内容所有这些引用。

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

这里有一个非常有用的设计模式。而不是实体中心的模式,为什么不把业务无关的基于布局的模式?描述页面上的项目从字面上,而不是语义模型。发现不需要关心,这是艾琳,96%的比赛,这是她的脸的照片。

我们的观点只关心有一个画面和两个文本字段需要填写。而不是从服务器接收整个用户对象和解析它在客户端上,我们教的服务器只给了我们完全一样多的信息,我们需要在屏幕上显示。想布局,而不是用户。


有了这个理念在心中,我们创建命名的作品为我们的团队的系统。我们的数据类的名称,而详细,准确地描述正在显示的内容。鉴于我们正在建立一个目录,这是我们的设计和工程团队之间的概念上的共享组件,这是很好的一目了然检查一个规范,准确地知道哪种布局,我们需要使用。像DetailedLayout_New名称只能带你到目前为止,我们从他们敬而远之完全。我们选择名称中使用像top_text或bottom_border位置属性的描述性分类我们的数据类和字段。无论使用哪种适合您。我们所有的布局模型都有一个共同的基类,你可以在文件的顶部看到定义LayoutData的,但我们会回来的什么短期内发生的事情。

我发现,当一个人的拨冗本做一些正确的事情,他们没有能力优先,而当有人花了时间做过去一些正确的事情,他们是伟大的先见之明的大师工匠一个完美主义者。-xkcd

这种可重复使用的视图数据系统允许服务器把它想成容器的任何类型的信息和应用程序本身只是起到一个管道的信息,接收的原始模型。然后,我们注入这些车型进入我们的ViewModels并结合他们使用的是Android的数据绑定我们的意见。188博金宝电子体育频道

如果我们要决定,明天,我们真的想使用该布局显示广告或信息的其它一些方式 - 我们将不需要的APK更新这样做。相反,我们只希望翻转服务器上的开关。听起来像变魔术一样?该死的附近是。讲起简单说一下服务器的工作方式。

哑客户端,智能服务器

构建一个薄的应用程序并不完全是神奇的,它不是要你或你的团队从保存在某个阶段编写复杂的代码。这里的想法是,我们已经提出的处理和业务逻辑的较大部分转移到比我们在标准的应用开发通常看到服务器。一个真正的产品时有一个多平台的团队,重复的逻辑经常(读:不可避免的)手段更多的错误。几年前,我们推出了扩展的性别和姿势的概念,以OkCupid平台。188bet金宝搏官网现在,虽然完全堪与崇高追求,所有这些选项及其经常被复杂的匹配逻辑组合爆炸使代码的培养皿中的所有类型的错误。过了一年,我们打捶痣堵塞漏洞在所有三个我们的平台(Android,iOS设备的桌面)。188博金宝电子体育频道最后,我们决定做的最好的事情是在服务器处理繁重工作,而客户端处理的显示。于是诞生了哑客户端的口头禅:“做什么样的客户端服务器可以做不做。”

在这里我不能足够强调常识的锻炼,而不是每一件事情需要在服务器上进行处理 - 这不是1970年,我们没有过线连接到主机发送的点击。我们只是确保我们的大多数业务逻辑都来自于服务器本身,使其不必重复写入。

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

建立了坚实的API是尽可能多的技术功能的问题,因为它是UX。坐下来与消费者和开发人员利用合理的请求和响应参数可持续终点是势在必行。但是,正如所有的代码,需求将随时间而改变。端点是没少受refactors比你的代码的其余部分。没有第二个选择是,版本的API吮吸。

我们创建了一个智能系统,为发现端点。客户端应用程序发送它可以支持(例如PictureTitleSubtitle,AvatarThreeText,TwoImageOneText)布局的枚举 - 服务器然后通过布局和填充在所述数据的优先级。

举个例子,如果你有一个旧的生成,只有附带的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对象作为鉴别上指定一个字段和解串器将检查该字段,并解析整个对象到类通过反射指定那里。

GSON处理使用一种叫做RuntimeAdapterFactory类多态反序列化。一旦您注册所有你想你的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:数据并不意味着奔忙如果你不能看到它 - 采用环氧

我们发送数据来回,事情看起来很大。那么,如何才能得到它在屏幕上?问好我的小弗伦,环氧从制作的Airbnb。环氧树脂本质上是类固醇Recyclerview。您提供环氧与要显示在屏幕上什么表示,它会处理大量繁重的工作需要得到它。对于发现,我们模型中的状态从LayoutData解释,我们在我们的API响应了和饲料,为这将反过来DIFF状态和更新的任何变化RecyclerView的EpoxyController。

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

有很多的好处,以这种方式使用RecyclerViews时,即使是相对静态的数据来获得。RecyclerViews懒洋洋地加载视图层次结构,以便布局时间与使用更少的内存本身更快,新加载的数据不会重复无效视图层次结构。环氧树脂本身的使用是相对简单的,所以我不会涵盖在这里深入。其主要依据是,我们申报滴胶资源标识符为我们的数据绑定的布局和它为我们生成的模型,我们可以声明添加到该处理RecyclerView的TypedEpoxyController。

Package-Info.java

在国家:一个简短的概要

这里就是我要带你去一个小旅程,通过发现MVI式的建筑风格的基石。在多,我们做开发的,什么是在屏幕上发生的概念化状态下,仅保持在视图中。例如说,我们加载了一些数据,并开始将运行,直到我们的数据操作已完成了微调。的该旋转器是否为活性仅保持在微调的状态小部件本身,而不是一个场在逻辑类像演示或视图模型。因此,准确地再现上类似的配置改变这种状态,或持续到存储将是一个艰巨的任务。

在上一节中,我们看到了我们如何能够通过一个“状态”,以环氧树脂,并使用该构建我们的数据的可视化表示。那么,什么是状态这里是什么意思?这是一个经常超载长期备有各种定义,但什么我指的是我们的应用程序的所有组件的当前状态的集中表现。这是没有比这票友。在科特林,我们可以把它表示我们会通过使用数据类的任何其他模型。这里心目中的重要方面,就是国家的性质保持不变。应该有你的状态类的属性没有制定者和属性本身应该是反光的所有数据,你会希望能够重新创建,坚持或报告。此规则遵守将使你有一个可预见的状态容器,它不受其他操作的副作用。

对于发现,我们的国家大多是由从我们的数据的反序列化派生我们的布局数据的实例。有特性包含在状态,像currentPageIndex,这可能不是从JSON本身,但仍描述应用程序当前状态的。

如果我们的应用程序的状态是不可变的数据类(免责声明:科特林瓦尔斯不definitionally一成不变的,但我们不必迂腐,对吧?),如何那我们来修改它与我们的应用程序的用户交互。我们已经做了彻底的步行通过我们的哑客户端的可视化组件解码的,所以让我们看看蜂鸣声和解码互动boops。

定义和解码互动

让我们暂停难以置信,并让自己获得假设了一下。如果有人问你描述你的应用程序提供的每一次互动,你会知道去哪里找?我们大多数人不会。现在说有一个方法可以轻松地定义一组可以采取一个应用程序内简洁和一贯的所有行动?剧透:有。双扰流警报:我没有拿出它。

FSA在左边,右边科特林版本。

从JavaScript的流量标准动作规格意识形态上借款,我们可以定义使用密封的数据类在我们的应用程序的所有交互。这里的超类是行动,而且我们所有的其他行动是由它派生的。然后,我们可以用这些行动来通知状态时,它需要用户交互的响应,内部事件或API调用来转换方式。

你的行为应该很容易阅读和人类,简单,发生了什么事或需要做的简单的描述写的。它们可以是包含内与相关信息不附加任何信息或数据的类的对象。无论哪种方式,如果我们用这个模型中,我们会看到,我们的一切应用程序可以做的强烈的想法告终。换句话说,这些行动是抽象和编码行为的有力工具。让我们来看看我们如何能够利用它们来修改我们的状态。

是更改要在该国见

所以,我们不那么假设行动,那么我们如何处理交互中的任何一个实例?我们这样做的时候,我们收到来自他人的行动,我们自然地做同样的事情,我们做出一些决定。我们需要通过控制流构建这些行动,将分析它,并决定如何处理的信息做。

在这样的单向数据流,我们可以调用构造减速,从我们的JavaScript表兄弟在世界做出反应的一个术语我借。减速机是使用全能科特林最容易模仿什么时候功能。

减速器是一个纯粹的函数,即返回的值(状态)仅由与没有可观察到的副作用的输入值(动作)确定的。我们的代码的不同部分不能随意从减速实例外改变我们的状态。减速机的外观的操作,然后使用这些数据包含有-内创建一个全新的状态。

你会经常听到人云亦云一个常见的情绪是复制状态是一个昂贵的和缓慢的操作。让我们花点时间来暂停并在此务实体现。我们只复制状态的互动 - 除非你正在创建数以百计的行动第二视频游戏,复制三立建状态正确标准化形状的影响将是难以察觉的。作为参考,复制和超过100款和大量布局的版本比较发现整个州的某个地方花了大约9ms的量级。那么下会导致在你的UI任何明显的延迟为60ms的JANK限制。它保留号码,如考虑到这一点,所以我们不运行进行预优化的风险是很重要的。在另一方面,具有真实性的单一来源为您的应用程序的状态使得开发和调试的方便很多地狱。

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

手边的业务,创建操作

如果我们在应用程序,一定要产卵长并行运行的进程或异步交互行为或事件,我们可以通过什么有时被称为ActionCreator通过他们。在形式ActionCreator是非常相似的减速与关键的区别是,它可以有副作用,但有国家本身不能直接访问。

使用控制流,我们解析操作,然后异步使用类似RxJava观测任务运行。当侧业务已经完成,例如在订阅拉姆达,我们可以派遣或者另一个或另一副作用诱导作用或将由减速被捕获并用于修改状态的最后行动。

所以,现在我们有一个如何来形容两者的视觉表现和我们的应用程序的行为的完整想法。还记得发现?让我们回到那个和看看我们如何编码和实现此行为。

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

我们需要反序列化,以便从JSON的行动能让他们的运行特征主要有效载荷一起走,并远程配置。使用我们用于布局相同的模式,我们使用多态反序列化解析JSON行动纳入其亚型通过场中的对象的JSON体辨别。

然后,我们必须绑定的,现在科特林,值对象视图本身。为了做到这一点,我们再次利用Android的数据绑定的力量和灵活性。188博金宝电子体育频道动作本身被定义为在视场中,从相同的JSON解析。在视图本身,我们定义交互模式,这将触发目前未知的行动,例如点击次数,长压力机,手势。一旦用户视图交互时,我们发信号给视图模型通过RxJava受火的作用。父视图模型保存参照国家,减速机,和ActionCreator分派该动作和状态被相应地修改。

这样做的好处是,一旦你的应用程序知道如何处理和改造其内部状态对于任何给定的行动 - 的观点本身是不可知的两个他们正在做什么和显示。恭喜你,你已经基本上只是做自己本机组件的Web浏览器!无论哪种方式,我们现在有一个愚蠢的客户端系统,我们可以远程配置上即时的布局和行为。

最后几点思考

希望有什么东西在这里给大家。有些概念很复杂,这是很难做到的他们一个公道,而在寻找从这样一个高层次的功能,但如果有足够的兴趣,我将在后续的后重温那些方面进行更深入。

这个系统也不是万能的,正如所有固执己见的编程决策,有取舍。但有一两件事我想澄清的是:让您的应用程序在这种方式瘦客户端操作并不意味着它不能脱机或缓存响应工作。减速机还是应该含有足够的逻辑来运行不需要接入互联网/服务器的所有内部操作。还有什么可以在客户端上依然存在,排队,直到重新建立连接,就像你有一个胖客户端。

重要的是要记住不能教条地坚持任何给定的体系结构或设计模式是很重要的,而是寻求创造性的解决方案,并推动创新的工程。请记住,架构无非是你和你的同胞开发商之间的合同,所以做什么工作(只记得记录吧!)最后,任何人都可以复制的代码,但真正的礼物,你可以给Android社区是你自己的观点188博金宝电子体育频道。我很想从你都听得见,所以随时跟踪和消息我的Twitter@BrandonJF

欢呼和编码快乐!