Android 应用架构—— 那些由于年轻犯的错


本系列文章旨在概述咱们搭建 Android 应用程序架构时可能会碰到的问题。我意识到,不管实现 Android app 架构的过程多么困难,结果证实这些必定是完成每个卓越的应用的基础。程序员

每种技术都有其天然的进化。或者更确切地说,它的社区经历了进化的过程。一个新的计算机语言或框架的早期采用者是爱好者,他们只是但愿掌握技术,并尽快完成一些工做。一般,新社区规模小,在开发人员之间的知识传递潜力有限,也就是说,每一个人都从本身的错误中学习,由于没有架构指南可用。sql

640?wx_fmt=jpeg

前言数据库

早期 Android 们的痛点:谷歌是否关心?编程

你能够说,有不少资深的家伙在不一样的技术上有不少的经验,但谁也没有时间提出标准。嗯,不必定。若是技术背后有一个强大的公司期望赚钱,他们的布道师会告诉你这个新语言多么酷,能够作不少事情,容易学习,可扩展,而且能够知足数以百万计的用户。网络

微软就常常用它的技术干这样的事情。另外一方面,当谷歌收购了 Android, 我真的觉得他们只是把它看成一个可有可无的项目。若是你从 Android 诞生之时就进入 Android 的世界,你必定记得 Google 根本不在意你的那种沮丧感。那些有额外的经验,能力和帮助社区的意愿的少数几我的如今是 Android 的超级明星或大神 —— 譬如 Jake Wharton。架构

“When Google bought Android, I honestly think they treated it just as some other side project.”app

640?wx_fmt=png

可能你会说,你没必要考虑太多架构和组织代码的事情,由于(Android)Framework 帮你作了。Android 强迫你把界面放到 Activity 中,可重用的界面放到 Fragment 中, 后台服务放到 Service 中,而且用 Broadcast Receiver 和其它组件通讯,这样可使你的生活变得更美好,是这样吗?不是的。框架

首先,有一些很好的实践和原则确实很好,与技术(平台)无关。例如,单一职责原则,依赖倒置原则,面向接口编程,杀死全局状态,尝试消灭全部状态,等等。ide

Framework 不多强迫你遵循原则。偏偏相反,它们以最坏的方式侵犯这些***实践和原则。想一想 Context 这个你处处使用的上帝对象(God object),各类单例管理者(singleton managers),拥有生命周期的 Fragment(那是怎样的噩梦呢),经常不能正确实现的 AsyncTask,它们吸着你 app 的血。单元测试

一个缺少指导的刚入行的开发者很容易造出一个怪物而不是一个 app。把它想象成一个意大利面条般的怪物吧 —— 这是一个不错的怪物,但不是一个好的 app。

译者注:意大利面条般的怪物寓指一团乱麻的代码

640?wx_fmt=jpeg

***,技术(technology)和 Framework 隐藏了 app 的用途。好的,这是一个 Android app,可是一个什么样的 Android app 呢?新闻阅读器?音乐播放器?语言学习程式?天气应用?也许是一个计划表。若是全部的东西都打包到由 Framework 提供的类,你就说不出(这是什么 app)。

正如 Robert Martin,也就是 Uncle Bob 说,“你的架构应该尖声喊出(scream)app 是作什么”,更技术一点说,业务逻辑应该清晰地分离,独立于 Framework。

Android 架构的四条黄金法

我但愿已经说清楚了,你不该当依赖 Framework 来使你的代码整洁有序,尤为是在编写 Android 代码的时候。咱们很早之前就意识到这点,但是缺少拿出牛逼解决方案的经验。架构失败要花不少时间才能表现出来,又不能够在项目中途改变整个架构。也不可能有时间将旧项目重构成新的、酷的、(想成为)***的架构。所以,咱们采起渐进的方法,慢慢地从一个项目到另外一个项目搭建咱们的架构,从咱们的错误中学习。咱们认为咱们的架构应该知足几个目标,它们是检验咱们的方法的标准。好的架构应该作到:

  1. 知足众多利益相关者的需求

  2. 支持关注点分离

  3. 逃离现实世界(Android、DB、Internet...)

  4. 使你的组件可测试

I.知足众多利益相关者

利益相关者(在这篇文章中)是任何对你的 app 开发感兴趣的人。除了开发,还有视觉设计师,交互设计师,项目经理,数据库管理员,测试等等。

固然,你不可能以某种方式组织你的代码,例如,交互设计师能够打开项目并当即了解全部内容,甚至能够进行一些修改。It's a unicorn.

640?wx_fmt=jpeg

个人意思是,你能够以这样一种方式组织你的代码,那个和交互设计师对接的程序员只须要打理和交互相关的代码。所以,全部交互被分离到那些负责交互的 classes / modules / components / whatever (组件)中,当处理 app 的交互部分时,只须要打理那些组件。

译者注:若是暂时不能理解利益相关者,不要紧的,看完本系列第三部分你就明白了。

II.支持关注点分离

我刚刚所说的就是一个关注点分离的例子。咱们支持这种特定的方法,由于它能很好地表达团队组织和项目阶段的配合,通常来讲,你的架构也应该支持关注点分离。关注点分离或者单一职责原则是指,每个组件应该只有一个变化的缘由。

III.逃离真实世界(Android、DB、Internet...)

逃离真实世界这条规则,先前已经说起。咱们曾说咱们想要尖声喊出 app 真正是作什么的,就是那样。咱们想要强调业务逻辑而且隐藏 Framework 的细节。这条规则应该更严格:不只要隐藏 Framework 的细节,并且要隐藏全部与外部世界相关的细节。

640?wx_fmt=jpeg

全部的肮脏的 Android 的东西,如传感器、通知机制、屏幕细节、数据库访问、互联网访问等。

IV.使你的组件可测试

你应该尽量地对你的 app 进行单元测试,而且你的架构应该容许你这样作。若是你不能测试全部东西,你至少应该覆盖你的业务逻辑。与真实世界分离能够很方便地作到这点。若是你的业务逻辑清晰地和 app 其他部分隔离,是很容易测试的。

640?wx_fmt=gif

***次迭代 —— 上帝 Activity

 
 
  1. public final class UsersActivity extends ListActivity { 

  2.  

  3. @Override 

  4. protected void onCreate(Bundle savedInstanceState) { 

  5. super.onCreate(savedInstanceState); 

  6.  //... 

  7.  new ListUsers().execute(); 

  8.  

  9. private final class ListUsers extends AsyncTask<Void, Void, Void> { 

  10.  

  11. @Override 

  12. protected Void doInBackground(Void... voids) { 

  13.  // final SQLiteOpenHelper sqLiteOpenHelper = ... 

  14.  // JsonObjectRequest jsObjRequest = new JsonObjectRequest 

  15.  // (Request.Method.GET, url, null, new Response.Listener<JSONObject>() { 

  16.  // MySingleton.getInstance(this).addToRequestQueue(jsObjRequest); 

  17.  // showData(user); 

  18.  return null

  19.  } 

  20.  } 

你可能在 “上古时代” 看到过这样的代码。若是没有,说明你很年轻。可是这一段代码哪里有问题呢?答案是哪里都有问题。

640?wx_fmt=png

咱们有一个 Activity 操做数据库,访问网络,解析数据,切换线程以及渲染数据。全部的利益相关者都在看这一个类,没有关注点是分离的,它是不可测试的,业务逻辑和 Android 的东西混杂在一块儿。

640?wx_fmt=jpeg

译者注:留意上图左边红色的标签。每一个标签分别对应一条黄金法则,红色表示不知足。SRP 是指单一职责原则,即分离关注点。

第二次迭代 —— MVP

***种方法显然是不能工做的。咱们尝试过的***件事情是 MVP,或者说 model-view-presenter。每一个人都熟悉 MVP。它是***的架构模式之一。看起来像这样:

640?wx_fmt=jpeg

这里,咱们分离了其实是 Android Fragment 的 View,咱们拥有表明咱们业务的(领域)模型,***咱们有协调一切的 Presenter。这确定是更好的。关注点有了一些分离,利益相关者再也不那么困惑,你也能够写一些单元测试了。尽管如此,因为 Presenter 直接操做数据库和全部一切,咱们仍然和真实世界混杂在一块儿。Presenter 成了上帝对象。它处理模型,将数据发送到视图,它拥有业务逻辑(业务逻辑是那些齿轮 :)),它访问数据库和网络,获取传感器数据,等等。因此,是好了些,但能够更好。

640?wx_fmt=jpeg

第三次迭代 —— MVP + managers

当政府不知道作什么的时候它会作什么?它成立一个代理机构。当开发不知道作什么的时候他们会作什么?他们引入一些 Manager。你不必定把它命名为 “*Manager” 。这些类有不少名字:uitls、helpers、fooBarBuzz-ator、等等。所以咱们引入 Manager。

640?wx_fmt=jpeg

说实话,这甚至有点凑效。业务逻辑包含在 Manager 中。利益相关者知道往哪看,关注点必定程度是分离的但能够作得更好,你能够编写更多的测试,但你依然直接触摸 Android ,因此你必须编写 Android 测试用例,并预先填写数据库来测试业务逻辑,一个字:不爽。

是的,Manager 有变成巨兽的倾向,很快就变得难以维护。你可能争论说它不会变得更复杂,你能够经过更简单的架构来更快地提供代码,但经过这种方法依然会有不少 BUG,可维护性也会遭到破坏。

640?wx_fmt=jpeg

译者注:留意 Manager this 和 Manager that 之间的标签

总结

在本系列的***部分,咱们经历了搭建实际可用的 Android 架构的挑战。良好的 Android 架构应该知足众多利益相关者的需求,支持关注点分离,强调业务逻辑,隐藏 Framework 的细节,并使你全部的组件均可以测试。在系列的第二部分,咱们将向你展现咱们如何管理对咱们有用的功能。在此以前,你是否有如何建立合适的 Android 工做流的建议?或者你遇到了什么问题?