Android 和 iOS 孰優孰劣:真實應用開發過程告訴你答案
英文原文: 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 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 上面。搞清楚怎么設置、怎么在線程安全的前提下,為每個用戶集中管理 PersistentStoreCoordinators 和 ManagedObjectContexts 也花了些功夫,最重要的是要支持多賬戶(這個話題可能需要另一篇博客來單獨講講)。為 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
- Official Android docs
- Professional Android 4 Application Development
- Android Recipes
- Android Database Programming
- Mobile Tuts+
- Google I/O 2012 app code
- StackOverflow
iOS APP
- Official iOS docs
- Beginning iOS 6 Development
- More iOS 6 Development
- Ray Wenderlich’s iOS 5 by Tutorials
- Ray Wenderlich’s iOS 6 by Tutorials
- StackOverflow
上面列出來的書其實用處很有限,因為跟大部分的技術類書籍一樣,書的內容都有點過時了,而且大部分書只停留在入門級別的概念介紹。不過,在一開始的前幾天看一下這些書,能夠比較快地理解平臺上的一些核心功能。就目前來講,對于這兩個平臺,在線資源仍然是最有價值的。
工具
接下來我只簡單說一下這兩個平臺的開發工具,因為關于這個話題已經有很多的討論。我不是 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 是一種非常自然的解決方案,因為它是基于面向對象里面兩個眾所周知的準則設計的 - 高內聚和低耦合。
通過 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 處理好的視圖的位置。
“復雜”布局
網頁開發上有一些常見的布局在 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 里面)。
在 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 上我用了 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/