Android 和 iOS 孰优孰劣:真实应用开发过程告诉你答案

jopen 10年前

英文原文: Android vs. iOS: Comparing the Development Process of the GQueues Mobile Apps

        随便搜索一下“Android vs. iOS”,都会出现很多关于哪个平台更好的争论,大多数的争论点都是关于市场占有率、易用性和设备分化 等问题。当然也有一些“以开发者的角度”去比较这两个平台的文章,但是很少有从技术上做深入的比较,通常也只是用一个简单的示例应用介绍一些基本的特性。 缺少这种深入的比较其实是有原因的:一个公司要做一个足够复杂的移动应用,通常需要一个人或团队做 Android,另外一个人或团队做 iOS。这两个平台使用不同的编程语言(Java 和 Objective-C),提供不同的 SDK,使用不同的开发工具,所以人力资源分配上各做各的平台也就不奇怪了。

        GQueues 是一个在线任务管理器,之前只有一个 HTML5 版本。最近我完成了 GQueues for Android 和 GQueues for iPhone & iPad 的开发。虽然这两个应用的复杂程度不能和第一人称射击游戏相提并论,但也绝不简单 – 为用户存储和管理数以千计的任务信息、支持多账户、提供到 WEB 端的后台同步、复杂的过滤、排序和分组功能。通过这次的实践,我希望透过独特的视角,分析和比较为这两个平台开发 GQueues 应用的过程。

Android 和 iOS 孰优孰劣:真实应用开发过程告诉你答案

        统计概况

  Android App iOS App
启动日期 Sept 21, 2012 Mar 2, 2013
第一个可测的 Beta 版本 Dec 22, 2012 June 10, 2013
应用发布日期 Jan 31, 2013 July 18, 2013
项目总耗时 4. 25 months 4. 5 months
Ramp Up Time 1 week 2 weeks
开发耗时 870 hours (approx) 960 hours (approx)
Beta 测试&Bugfix 34 days 38 days
Beta 测试人员人数 92 people 48 people
代码行数 26,981 lines 23,872 lines
应用大小 1. 1 MB 3. 5 MB
视频预览 GQueues for Android Video GQueues for iOS Video

        学习曲线

        我已经写了 12 年的代码,但这是我写的第一个 Android 应用,也是我写的第一个偏向数据处理的 iOS 应用(2010 年我做过两个 iOS 3 上的游戏,但那两个游戏主要只涉及一些动画和蓝牙连接)。 我最后一次用 Java 是在研究生阶段,而我的 Objective-C 也仅限于那两个游戏。所以对于这两个平台,我基本上可以算是从零开始。

        简单讲,只需要花一半学习 iOS 的时间来学习 Android,我就能开始 Android 开发。对于 Android,我花了一周时间用来看书、跟着一些教程做一些测试应用,这些测试应用包含了 GQueues 将会用到的一些核心功能。做完这些,我基本上算是打好了为 GQueues 设计架构的基础,同时也可以开始为这个项目写代码了。在接下来的一周我可以很轻松自如地基于 Android 做开发,而不再需要依赖某个资源去实现新特性了。

        对于 iOS,我同样按照上面的流程,但我花了两周时间做各种测试/实验,才让自己觉得可以开始为这个项目写一些基础代码了。其中大部分的时间都花在研究 CoreData 各种复杂的 API 上面。搞清楚怎么设置、怎么在线程安全的前提下,为每个用户集中管理 PersistentStoreCoordinatorsManagedObjectContexts 也花了些功夫,最重要的是要支持多账户(这个话题可能需要另一篇博客来单独讲讲)。为 FetchedResultsControllers 开发一个可扩展的架构花了更多时间,FetchedResultsControllers 用于支持可被用户查看以及操作的任务表单、队列和分类。最后又过了两周(总共花了一个月)自己才能比较轻松自如地基于 iOS 写代码。

        总的来说,Android 的文档(官方文档、第三方教程、图书、代码示例、StackOverflow)质量都非常高。我从一些著名的开源 Android 应用中学到了很多架构上的最佳实践,如 Google 开放给开发者的 2012 Google I/O app。此外,Android 本身就是开源的,必要时我可以自己查看 Android 的平台代码,弄清楚一些疑难问题。虽然 iOS 也有很多文档,但由于 iOS5 和 iOS6 相比之前的版本改动非常大,大部分文档都已经过时,其中包括 ARC 入门一文(introduction of Automatic Reference Counting)。因此,大部分的示例代码(包括 Apple 官方示例)和一些问题的解决方法都是不正确的,需要使用新的方法取而代之。搞清楚这些肯定也需要花更多的时间。

        从上面的统计表中也可以看出,开发 GQueues for Android 要比开发 iOS 版的快十分之一的时间,尽管在开发 Android 版的期间我重新实现了之前用于支持 GQueues HTML5 版的整个后端服务器同步代码。而开发一个不采用原始 iOS6 风格 UI 的应用也需要多花些时间,单单比较这个数据,Android 开发就是比 iOS 开发快。

        用到的资源

        Android App

        iOS APP

        上面列出来的书其实用处很有限,因为跟大部分的技术类书籍一样,书的内容都有点过时了,而且大部分书只停留在入门级别的概念介绍。不过,在一开始的前几天看一下这些书,能够比较快地理解平台上的一些核心功能。就目前来讲,对于这两个平台,在线资源仍然是最有价值的。

        工具

        接下来我只简单说一下这两个平台的开发工具,因为关于这个话题已经有很多的讨论。我不是 Eclipse 或者 XCode 的脑残粉,它们有各自的强项和弱点(其实我最喜欢的还是 Vim)。Eclipse 的搜索暴慢而且很繁琐。XCode Organizer 的文档搜索也卡爆了。Eclipse 中使用 log tags(通过 Android 插件的 logcat 集成)过滤日志超级实用。两个 IDE 的代码补全都很不错,XCode 的 Interface Builder 一点用处都没有(后面细讲)。不过 XCode Instruments 就非常有用了,可以用它做优化分析、调试等等。我开始做 GQueues for Android 的时候,Google 还没发布 Android Studio,不过在 GQueues 的后续更新版本中我会拿它来试试。

        如果你一边写代码一边测试,用 Android 的模拟器简直就是浪费时间(真不敢相信它能慢成这个鸟样)。在开发过程中,我都是直接部署到真机上测试的,用真机快很多。iOS 的模拟器则很不同,跟 Android 相比简直就是火箭跟蜗牛赛跑,这也让整个开发过程更加高效。每写一小段代码我都会在模拟器上跑一下,等到整个功能完成了我就会部署到真机上玩玩。

        对于 Android,我有各个版本的测试机器(除了 Gingerbread,即 Android 2.3),除此之外,就要倚靠 beta 测试过程中各种设备的覆盖了。对于 iOS 来讲就要简单很多了,我只需要拿 GQueues 需要支持的最旧的和最新的机器来测试就够了。

        测试设备

        Android 设备

  • Samsung Infuse (Froyo 2.2)
  • Nexus S (ICS 4.0.3)
  • Galaxy Nexus (Jelly Bean 4.2)
  • Samsung Galaxy Tab 10.1 (Honeycomb 3.2)
  • Nexus 7 (Jelly Bean 4.2)

        iOS 设备

  • iPhone 4 (iOS 6)
  • iPhone 5 (iOS 6)
  • iPad 2 (iOS 6)
  • iPad 4th generation (iOS 6)

        设计

        布局

        GQueues 的其中一个需求就是必须同时支持任意尺寸的手机和平板,并且针对不同的表单元素进行优化布局。由于各种各样的设备都运行着 Android 系统,Android 也理所当然地有着成熟的 UI 组件帮助开发者支持各种尺寸。例如从 Android 第一个版本开始,RelativeLayout 提供了 View 之间相对布局的支持,可用于创建灵活、响应迅速的布局。另外,在 Android 中所有的布局都由 XML 定义,这设计界面的方式非常简洁、简单并且高效,试过 iOS 中创建布局之后这种体会就更加深刻了。

        相对于 Android 的 RelativeLayout,iOS 有 Auto Layout,这种布局方式比较新(iOS 6 新引入的),集成到了 Interface Builder (IB)中,但是太难用了。我花了好多天学习 IB 中怎么用 Auto Layout,跟任何 iOS 6 开发者一样,仅靠 IB 为视图(View)设定各种精确的约束,完全改变了我自己的标准,这是因为 IB 所谓的“智能”系统时刻维持(纠正)着视图布局相对位置。我学了很多技巧,想着弥补 IB 的短板,但是没啥作用。最后我只能放弃 IB,转而用冗长的代码实现所有布局。如果你放弃 IB 和富有极客范的 ASCII art style 来写布局,使用 Auto Layout 来实现还是很强大、很直接的。希望苹果在 iOS 7 中已经改善这些,不过我还木有试过。

        如果一个应用需要同时针对小屏设备和大屏设备进行优化,最关键的就是基于屏幕的真实尺寸进行动态组合视图,这种方式被称作“适配性布局 (Adaptive Layout)”,平板电脑可以在一屏中显示两个或三个视图,而手机上一屏则只显示一个视图。Android 通过 Fragments 支持这种设计,Fragment 是一个独立的、自包含的的模块,能够在需要的时候直接丢到 Activity 中去用。通过使用 Fragments,只需要调整几行 XML 代码就可以让 GQueues 的布局适配不同分辨率的屏幕。对于我来讲,Fragments 是一种非常自然的解决方案,因为它是基于面向对象里面两个众所周知的准则设计的 - 高内聚和低耦合。

Android 和 iOS 孰优孰劣:真实应用开发过程告诉你答案

        通过 Custom Container View Controller(你也可以用 Master-Detail 模板,当然这种方式宽度是固定的,也不支持个性化定制),iOS 支持一屏使用多个 ViewController。对于这个不成熟的特性,我觉得 Apple 的文档显得很复杂和不完整,最好的资源还要数 Ray’s iOS5 tutorials 和 WWDC 视频。我花了比预计要多的时间,终于搞好了在 iPad 上同时显示多个 View、在 iPhone 上显示单个 View 的布局架构。

        设备翻转

        简单说,在 Android 上支持设备翻转需要做很多工作,这些工作也是最终导致很多 bug 的源头,而在 iOS 上,支持屏幕翻转只需要做一点点工作,剩下就是系统帮我们搞定了。在 Android 上,屏幕翻转会直接销毁现有整个视图栈(Activity 栈),屏幕翻转完成后再重建每个视图。所以在 GQueues 中支持屏幕翻转,我需要无时无刻保存好所有当前状态,随时保证翻转后能正常恢复状态。而在 iOS 上,系统会帮你管理所有屏幕翻转相关的细节,唯一需要我关心的就是翻转之后,我需要调整那些没有被 Auto Layout 处理好的视图的位置。

        “复杂”布局

Android 和 iOS 孰优孰劣:真实应用开发过程告诉你答案

        网页开发上有一些常见的布局在 GQueues 上实现起来非常困难,不管是 Android 还是 iOS。其中一个例子是在任务详细界面显示标签。每个标签都是变长的,在必要时标签需要自动换行。在网页上实现这个只需要设置 CSS 的 float 值就可以了。但不管是 Android 还是 iOS 对这种“流式布局”(Flow Layout)都没有原生的支持,这也意味着我需要写很多代码自己去计算和摆放这些标签,以达到“流式布局”的效果。最后 Android 的代码是基于 Romain Guy 的演讲内容和 Artem Votincev 的 flow layout 实现的。在 iOS 上也采用了类似的方法,基于容器的总宽度,计算每个标签的宽度,最后设置 auto layout 的参数。对于这个布局的实现在两个平台上都耗了很大的工作量。

        旧设备支持

        关于 Android 的生态系统常被人吐槽的就是严重的系统分化。运营商推送更新的步伐总是很慢,所以现在仍有大量运行着旧系统的设备,这也就意味着如果要保证应用足够大的设 备覆盖率,开发者就不能使用新版系统带来的新特性。不过好在现在针对这个问题,Android 社区做了很大的努力,提供了一些用于在旧系统上支持新特性的库。通过使用 Android 官方的 Support Library 和 Jake Wharton 的 ActionBarSherlock Library,我几乎可以在 Android 2.2 上使用 Jelly Bean(4.2)中所有的新特性。

        对于 iOS 来说,支持旧系统一说几乎不存在,或者说根本就不是关键。在准备阶段我花了一些时间考虑从哪个 iOS 版本开始支持,而当时的统计数据显示使用 iOS 6 系统的设备已经达到83%, 而当时对于放弃支持 iPad 一代我也有一些疑虑,因为我老爸老妈老姐用的就是 iPad 一代,他们将是 GQueues 的铁杆支持者。最后我决定还是只支持 iOS 6+,这样我可以放开手使用 Auto Layout,而不需要浪费大量时间实现任何过时的布局技术。当然,我解决了 iPad 一代的问题(至少对我家里人说来说已经解决),就是换掉他们的 iPad 一代,给他们每人买一个 iPad 四代(作者有钱银)。

        架构

        数据存储和管理

        对于 GQueues 来说,数据是核心 - 把数据保存到设备上然后同步到 WEB 端。Android 和 iOS 有着完全不同的数据管理系统。Android 提供了 ContentProvider,它是 SQLite 数据库上层的一个可被继承的应用接口,作为一个结构化框架被用于所有应用的数据处理。ContentProvider 学习起来比较难,搞定一个 GQueues 可用的实现,前期需要花很多工作。一旦搞定了第一步后面的扩展和个性化定制都变得简单多了。

        一些背景信息,GQueues 的 web service 是基于 Google App Engine’s Datastore 的,这是一个高扩展性的分布式 NoSQL 存储系统,而 SQLite 则是一个标准的关系型数据库,扩展性明显也比较差,但这完全不需要考虑,因为这个应用只存储一个用户的数据。(顺便说一下,架构上我采用了“一个用户对应 一个数据库”的设计,这对于快速简单地实现多用户切换有重要意义,不过实现细节可能得再开一博来聊了)。不管怎么说,Android 的一个很大的优点就是可以创建 SQLite Views 来支持 Smart Queues。为了支持 Smart Queues,搞清楚各种复杂的表关联查询和子查询也花了写功夫,但是这也让 Smart Queues 的加载更加高效和快速,因为过滤不是在代码里面实现的(在 SQL 里面)。

Android 和 iOS 孰优孰劣:真实应用开发过程告诉你答案

        在 iOS 上,我用的是 Core Data, 它是 iOS 上的“schema 驱动数据图形管理和持久化框架”,基本上它可以被看做是一个 NoSQL 存储,不过有趣的是,Core Data 背后实际上是 SQLite 数据库(呃…实际上 SQLite 也是几个可选项中最合理的选择)。iOS 也允许用户直接创建 SQLite 数据库,但只支持通过纯C代码来操作,对于其他 iOS 组件没有原生集成。Core Data 的学习起来也比较困难,但最后我还是选择 Core Data 而不用 SQLite,因为这样我可以轻松实现很多功能,包括缓存、数据模型迁移支持,还有通过 NSFetchedResultsController,可以非 常简单地为界面中的 table(列表)提供数据。

        管理数据集的关键就是使用事务,尤其重要是做数据同步的时候 - ACID,即:atomic(原子性)、consistent(一致性)、 isolated(隔离性)、durable(持久性)。Android 上实现事务似很直观,跟大部分关系型数据库管理系统的实现方式是一样的,因此,保证数据完整性并不困难。另外,用好 SQLite 中的 UNIQUE ON CONFLICT REPLACE 语句,在数据同步的过程中建表、对记录进行原子更新的时候几乎不需要做任何额外工作。

        严格来讲,Core Data 并不完全支持事务。通过使用单独的子 ManagedObjectContexts 做后台线程处理,再加上@synchronized,能够处理好数据更新和同步,同时避免不正确的写操作覆盖(overwrite)。关于高效更新和创建对象,iOS 给的建议帮助很小,总的来说,CoreData 给我的赶脚很笨重,并没有它声称的那么好用。另外,在 Android 上,SQLite 可以轻松实现快速加载 Smart Queues,而在 iOS 上,所有的过滤都必须在代码中实现,就算用了大量的缓存,速度仍然很慢。

        搜索

        在 GQueues for Android 上增加强大的全文搜索功能很简单,我模仿 Google I/O应用里面的搜索实现,使用了 SQLite 的 FTS3特性。首先创建一个虚拟表,然后在一个存储了用户任务的表上设置几个触发器,由这些触发器填充数据到虚拟表。做完了这些,剩下的就是设计一个搜索界面和为搜索历史添加存储。

        iOS 的 Core Data 对于全文搜索并没有原生支持,所以我通过在谓词(Predicate)中使用 LIKE 语句,实现基本的任务描述和日记的搜索功能。这个实现当然没有全文搜索那么强大,但我认为它已经能够覆盖现实生活中大部分的使用场景了。

        特性 API

        用于比较,我只会列举在 GQueues 中使用到的几个 API。

        快速添加(Quick Add)

        正则表达式在实现 GQueues 中 Quick Add 解析的时候扮演着一个非常重要的角色,幸运的是,Android 和 iOS 对于正则表达式都有着原生的支持。Android 中的 Pattern 和 Matcher 从第一个版本起就开始支持,同时也包含了很多正则语法,其中包括前向断言(look-ahead assertion)和后向断言(look- behind assertion)。iOS 则从 iOS 4 开始引入 NSRegularExpression 类,令人高兴的是,我可以把我在 Android 上辛辛苦苦写好的正则表达式几乎原封不动地搬到了 iOS 上。

        分页

Android 和 iOS 孰优孰劣:真实应用开发过程告诉你答案

        在设计界面的时候,我希望用户在查看任务详细的时候左右滑动切换。在 Android 上我用了 ViewPager 和新的 FragmentStatePagerAdapter 类,FragmentStatePagerAdapter 还处于试验阶段,并且只能通过支持库(Support Library)来使用。我花了几天的时间实现了一个绑定好数据的初级版本,同时解决了几个关于重 复菜单项的 bug 和在数据发生变化后的处理。这些比我预想的要困难很多,要不是因为左右滑动切换任务的用户体验那么好的话,我真不想实现这个功能。iOS 上的 UIPageViewController 就简单很多了,虽然也有一些奇怪问题要解决,并且需要自己再加上缓存支持使滑动复杂视图的时候达到可用状态。

        语音输入

        Android 提供了了一个先进但很容易使用的 speech-to-text API,只用 20 行代码,我就把 RecognizerIntent 集成到 GQueues,提供了一个自定义的语音输入功能。但很遗憾,iOS 并没有提供支撑 SIRI 背后技术的 API,开发者只能使用第三方库,依赖键盘上的麦克风提供语音输入的支持。我找了各种第三方库,包括 Nuance - SIRI 语音识别的提供商,但发现没有免费版本,收费版本价格不菲。所以最后 GQueues 只能靠用户自己使用键盘上内置的麦克风选项来进行语音输入,其实这也已经足够了,只要用户还记得有这么个功能。

        分享/插件(小部件)

        通过使用 Intent,在 Android 上可以很容易就可以把我的应用集成到安装在用户手机上的其他应用。同样地,只需要很少的代码,通过支持 ACTION_SEND intent,我就能够让用户在其他应用中创建 GQueues 任务。Android 同时也提供了一个小部件平台,于是我也做了几个小部件,以后还会增加一些。iOS 对于跨应用集成和桌面小部件的支持度为零,完全不支持这两个功能。

        测试和发布

        beta 测试

        在上面的统计概况表中已经指出,beta 版面向真实用户测试了一个多月。两组测试人员都非常棒,帮我找到了数十计的 bug,提出了增加一些特性的建议,对一些 UI 上不合理的地方提出了反馈。我通过私有的 Google Group 组织 beta 测试,这样的 beta 测试保证了最后发布的应用对人们是真正有用的。在每次 beta 测试的最后,通过调查问卷我收集到了很多有建设性的反馈,也帮助进一步判断我的应用是否达到了可发布的状态。

        让测试者开始测试只需要发个 APK 的链接,让他们下载到他们机器上(呃..他们还需要在设置界面中开启“允许安装 Google Play 以外的应用”的选项)。Google 很方便地支持用真实用户来进行 alpha 和 beta 测试,可在开发者控制台和阶段推广中进行设置。在未来的版本更新中我想用用这两个功能。

        iOS 中的 beta 测试困难得多,就算用了 TestFlight 服务,虽然 TestFlight 很大程度地简化了流程。为了满足 Apple 的控制欲,每部测试设备的 UUID 都要加到用于签名 beta 版应用的证书当中。因此,每次要添加 beta 测试者的时候,不论是添加一个人还是一群人,我都需要重新 build 一遍我的 app。除此之外,Apple 还限制了你一年最多只能注册 100 个测试设备。所以我要小心利用好这 100 个坑,这也是为什么 GQueues 的 iOS 测试者只有 Android 的一半。

        发布

        当然,不谈谈发布流程,Android 和 iOS 的比较都不算完,在 Google Play 上发布 GQueues 是一件很好玩的事情,只要我认为已经准备好了,我随时可以发布我的应用。点下按钮之后,30 分钟内,我的应用就能在 Google Play 上被全世界的用户找到并安装到他们的设备上。而在 App Store 上发布一款应用,相信每个 iOS 开发者都有同样的感受,那是一个令人感到郁闷的经历。经过了数月紧张严密的编码,我只能把我的创作提交给 Apple,然后等 7 天,7 天之后审核人员花 2 分钟看看我的应用,最后拒绝了我的提交。我只能按要求做了修改之后再次提交,我又得等 8 天才在最后通过了审核。当然还有很多关于提交应用到 App Store 的恐怖故事,跟他们比起来,我就像是公园里逛了一圈。尽管如此,在自己的商业控制上要对这样一个“情绪化的第三方平台”做出那么多的让步,仍然让我觉得很 不爽。

        获胜的平台

        从上面的分析来看,做 GQueues 的过程中,并没有出现平台A完胜平台B的情况。Android 和 iOS 在某些领域各有千秋,也都有需要改进的地方。从这两个平台的历史来看,貌似目前 Android 势头更猛一些,不止体现在市场占有率上,而是看到了 Android 近两年在 UI 上的改进和开发平台的稳步提升。而 Apple 则是封闭的王者,我也坚信他们在很努力地做着他们认为是下一代移动计算革命的事情。不管怎么说,当我想想这 6 年间所兴起的 app 生态圈,我为自己在这个移动技术快速更新的时代,能在这两个平台上做开发感到荣幸。

        原文链接: GQueues   翻译: 伯乐在线 - neevek

        译文链接: http://blog.jobbole.com/54050/