Android和iOS孰優(yōu)孰劣:真實應用開發(fā)過程告訴你答案
隨便搜索一下“Android vs. iOS”,都會出現(xiàn)很多關于哪個平臺更好的爭論,大多數(shù)的爭論點都是關于市場占有率、易用性和設備分化等問 題。當然也有一些“以開發(fā)者的角度”去比較這兩個平臺的文章,但是很少有從技術上做深入的比較,通常也只是用一個簡單的示例應用介紹一些基本的特性。缺少 這種深入的比較其實是有原因的:一個公司要做一個足夠復雜的移動應用,通常需要一個人或團隊做Android,另外一個人或團隊做iOS。這兩個平臺使用 不同的編程語言(Java和Objective-C),提供不同的SDK,使用不同的開發(fā)工具,所以人力資源分配上各做各的平臺也就不奇怪了。
GQueues是一個在線任務管理器,之前只有一個HTML5版本。最近我完成了GQueues for Android 和GQueues for iPhone & iPad 的開發(fā)。雖然這兩個應用的復雜程度不能和***人稱射擊游戲相提并論,但也絕不簡單 – 為用戶存儲和管理數(shù)以千計的任務信息、支持多賬戶、提供到WEB端 的后臺同步、復雜的過濾、排序和分組功能。通過這次的實踐,我希望透過獨特的視角,分析和比較為這兩個平臺開發(fā)GQueues應用的過程。
統(tǒng)計概況
Android App | iOS App | |
啟動日期 | Sept 21, 2012 | Mar 2, 2013 |
***個可測的Beta版本 | Dec 22, 2012 | June 10, 2013 |
應用發(fā)布日期 | Jan 31, 2013 | July 18, 2013 |
項目總耗時 | 4.25 months | 4.5 months |
Ramp Up Time | 1 week | 2 weeks |
開發(fā)耗時 | 870 hours (approx) | 960 hours (approx) |
Beta測試&Bugfix | 34 days | 38 days |
Beta測試人員人數(shù) | 92 people | 48 people |
代碼行數(shù) | 26,981 lines | 23,872 lines |
應用大小 | 1.1 MB | 3.5 MB |
視頻預覽 | GQueues for Android Video | GQueues for iOS Video |
下載 | ![]() |
![]() |
學習曲線
我已經(jīng)寫了12年的代碼,但這是我寫的***個Android應用,也是我寫的***個偏向數(shù)據(jù)處理的iOS應用(2010年我做過兩個iOS 3上的 游戲,但那兩個游戲主要只涉及一些動畫和藍牙連接)。 我***一次用Java是在研究生階段,而我的Objective-C也僅限于那兩個游戲。所以對于 這兩個平臺,我基本上可以算是從零開始。
簡單講,只需要花一半學習iOS的時間來學習Android,我就能開始Android開發(fā)。對于Android,我花了一周時間用來看書、跟著一 些教程做一些測試應用,這些測試應用包含了GQueues將會用到的一些核心功能。做完這些,我基本上算是打好了為GQueues設計架構的基礎,同時也 可以開始為這個項目寫代碼了。在接下來的一周我可以很輕松自如地基于Android做開發(fā),而不再需要依賴某個資源去實現(xiàn)新特性了。
對于iOS,我同樣按照上面的流程,但我花了兩周時間做各種測試/實驗,才讓自己覺得可以開始為這個項目寫一些基礎代碼了。其中大部分的時間都花在研究CoreData各種復雜的API上面。搞清楚怎么設置、怎么在線程安全的前提下,為每個用戶集中管理PersistentStoreCoordinators和ManagedObjectContexts也花了些功夫,最重要的是要支持多賬戶(這個話題可能需要另一篇博客來單獨講講)。為FetchedResultsControllers開發(fā)一個可擴展的架構花了更多時間,FetchedResultsControllers用于支持可被用戶查看以及操作的任務表單、隊列和分類。***又過了兩周(總共花了一個月)自己才能比較輕松自如地基于iOS寫代碼。
總的來說,Android的文檔(官方文檔、第三方教程、圖書、代碼示例、StackOverflow)質(zhì)量都非常高。我從一些著名的開源 Android應用中學到了很多架構上的***實踐,如Google開放給開發(fā)者的2012 Google I/O app。此外,Android本身就是 開源的,必要時我可以自己查看Android的平臺代碼,弄清楚一些疑難問題。雖然iOS也有很多文檔,但由于iOS5和iOS6相比之前的版本改動非常 大,大部分文檔都已經(jīng)過時,其中包括ARC入門一文(introduction of Automatic Reference Counting)。因此,大部分的示例代碼(包括Apple官方示例)和一些問題的解決方法都是不正確的,需要使用新的方法取而代之。搞清楚這些肯定也需要花更多的時間。
從上面的統(tǒng)計表中也可以看出,開發(fā)GQueues for Android要比開發(fā) iOS 版的快十分之一的時間,盡管在開發(fā)Android版的期間我重新實現(xiàn)了之前用于支持GQueues HTML5版的整個后端服務器同步代碼。而開發(fā)一個不 采用原始iOS6風格UI的應用也需要多花些時間,單單比較這個數(shù)據(jù),Android開發(fā)就是比iOS開發(fā)快。
用到的資源
上面列出來的書其實用處很有限,因為跟大部分的技術類書籍一樣,書的內(nèi)容都有點過時了,而且大部分書只停留在入門級別的概念介紹。不過,在一開始的前幾天看一下這些書,能夠比較快地理解平臺上的一些核心功能。就目前來講,對于這兩個平臺,在線資源仍然是最有價值的。
工具
接下來我只簡單說一下這兩個平臺的開發(fā)工具,因為關于這個話題已經(jīng)有很多的討論。我不是Eclipse或者XCode的腦殘粉,它們有各自的強項和 弱點(其實我最喜歡的還是Vim)。Eclipse的搜索暴慢而且很繁瑣。XCode Organizer的文檔搜索也卡爆了。Eclipse中使用 log tags(通過Android插件的logcat集成)過濾日志超級實用。兩個IDE的代碼補全都很不錯,XCode的 Interface Builder一點用處都沒有(后面細講)。不過XCode Instruments就非常有用了,可以用它做優(yōu)化分析、調(diào)試等等。 我開始做GQueues for Android的時候,Google還沒發(fā)布Android Studio,不過在GQueues的后續(xù)更新版本中我會 拿它來試試。
如果你一邊寫代碼一邊測試,用Android的模擬器簡直就是浪費時間(真不敢相信它能慢成這個鳥樣)。在開發(fā)過程中,我都是直接部署到真機上測試 的,用真機快很多。iOS的模擬器則很不同,跟Android相比簡直就是火箭跟蝸牛賽跑,這也讓整個開發(fā)過程更加高效。每寫一小段代碼我都會在模擬器上 跑一下,等到整個功能完成了我就會部署到真機上玩玩。
對于Android,我有各個版本的測試機器(除了Gingerbread,即Android 2.3),除此之外,就要倚靠beta測試過程中各種設備的覆蓋了。對于iOS來講就要簡單很多了,我只需要拿GQueues需要支持的最舊的和***的機器來測試就夠了。
測試設備
|
|
設計
布局
GQueues的其中一個需求就是必須同時支持任意尺寸的手機和平板,并且針對不同的表單元素進行優(yōu)化布局。由于各種各樣的設備都運行著 Android系統(tǒng),Android也理所當然地有著成熟的UI組件幫助開發(fā)者支持各種尺寸。例如從Android***個版本開 始,RelativeLayout提供了View之間相對布局的支持,可用于創(chuàng)建靈活、響應迅速的布局。另外,在Android中所有的布局都由XML定 義,這設計界面的方式非常簡潔、簡單并且高效,試過iOS中創(chuàng)建布局之后這種體會就更加深刻了。
相對于Android的RelativeLayout,iOS有Auto Layout,這種布局方式比較新(iOS 6新引入的),集成到了 Interface Builder(IB)中,但是太難用了。我花了好多天學習IB中怎么用Auto Layout,跟任何iOS 6開發(fā)者一樣,僅靠 IB為視圖(View)設定各種精確的約束,完全改變了我自己的標準,這是因為IB所謂的“智能”系統(tǒng)時刻維持(糾正)著視圖布局相對位置。我學了很多技 巧,想著彌補IB的短板,但是沒啥作用。***我只能放棄IB,轉而用冗長的代碼實現(xiàn)所有布局。如果你放棄IB和富有極客范的 ASCII art style來寫布局,使用Auto Layout來實現(xiàn)還是很強大、很直接的。希望蘋果在iOS 7中已經(jīng)改善這些,不過我還木有試 過。
如果一個應用需要同時針對小屏設備和大屏設備進行優(yōu)化,最關鍵的就是基于屏幕的真實尺寸進行動態(tài)組合視圖,這種方式被稱作“適配性布局 (Adaptive Layout)”,平板電腦可以在一屏中顯示兩個或三個視圖,而手機上一屏則只顯示一個視圖。Android通過Fragments 支持這種設計,F(xiàn)ragment是一個獨立的、自包含的的模塊,能夠在需要的時候直接丟到Activity中去用。通過使用Fragments,只需要調(diào) 整幾行XML代碼就可以讓GQueues的布局適配不同分辨率的屏幕。對于我來講,F(xiàn)ragments是一種非常自然的解決方案,因為它是基于面向對象里 面兩個眾所周知的準則設計的 - 高內(nèi)聚和低耦合。
通過Custom Container View Controller(你也可以用Master-Detail模板,當然這種方式寬度是固定的, 也不支持個性化定制),iOS支持一屏使用多個ViewController。對于這個不成熟的特性,我覺得Apple的文檔顯得很復雜和不完整,***的 資源還要數(shù)Ray’s iOS5 tutorials和WWDC視頻。我花了比預計要多的時間,終于搞好了在iPad上同時顯示多個View、在 iPhone上顯示單個View的布局架構。
設備翻轉
簡單說,在Android上支持設備翻轉需要做很多工作,這些工作也是最終導致很多bug的源頭,而在iOS上,支持屏幕翻轉只需要做一點點工作, 剩下就是系統(tǒng)幫我們搞定了。在Android上,屏幕翻轉會直接銷毀現(xiàn)有整個視圖棧(Activity棧),屏幕翻轉完成后再重建每個視圖。所以在 GQueues中支持屏幕翻轉,我需要無時無刻保存好所有當前狀態(tài),隨時保證翻轉后能正?;謴蜖顟B(tài)。而在iOS上,系統(tǒng)會幫你管理所有屏幕翻轉相關的細 節(jié),唯一需要我關心的就是翻轉之后,我需要調(diào)整那些沒有被Auto Layout處理好的視圖的位置。
“復雜”布局
網(wǎng)頁開發(fā)上有一些常見的布局在GQueues上實現(xiàn)起來非常困難,不管是Android還是iOS。其中一個例子是在任務詳細界面顯示標簽。每個標 簽都是變長的,在必要時標簽需要自動換行。在網(wǎng)頁上實現(xiàn)這個只需要設置CSS的float值就可以了。但不管是Android還是iOS對這種“流式布 局”(Flow Layout)都沒有原生的支持,這也意味著我需要寫很多代碼自己去計算和擺放這些標簽,以達到“流式布局”的效果。***Android 的代碼是基于Romain Guy的演講內(nèi)容和Artem Votincev的flow layout實現(xiàn)的。在iOS上也采用了類似的方法,基于容器的總寬度,計算每個標簽的寬度,***設置auto layout的參數(shù)。對于這個布局的實現(xiàn)在兩個平臺上都耗了很大的工作量。
舊設備支持
關于Android的生態(tài)系統(tǒng)常被人吐槽的就是嚴重的系統(tǒng)分化。運營商推送更新的步伐總是很慢,所以現(xiàn)在仍有大量運行著舊系統(tǒng)的設備,這也就意味著 如果要保證應用足夠大的設備覆蓋率,開發(fā)者就不能使用新版系統(tǒng)帶來的新特性。不過好在現(xiàn)在針對這個問題,Android社區(qū)做了很大的努力,提供了一些用 于在舊系統(tǒng)上支持新特性的庫。通過使用Android官方的 Support Library和Jake Wharton的ActionBarSherlock Library,我?guī)缀蹩梢栽贏ndroid 2.2上使用Jelly Bean(4.2)中所有的新特性。
對于iOS來說,支持舊系統(tǒng)一說幾乎不存在,或者說根本就不是關鍵。在準備階段我花了一些時間考慮從哪個iOS版本開始支持,而當時的統(tǒng)計數(shù)據(jù)顯示使用iOS 6系統(tǒng)的設備已經(jīng)達到83%, 而當時對于放棄支持iPad一代我也有一些疑慮,因為我老爸老媽老姐用的就是iPad一代,他們將是GQueues的鐵桿支持者。***我決定還是只支持 iOS 6+,這樣我可以放開手使用Auto Layout,而不需要浪費大量時間實現(xiàn)任何過時的布局技術。當然,我解決了iPad一代的問題(至少對我 家里人說來說已經(jīng)解決),就是換掉他們的iPad一代,給他們每人買一個iPad四代(作者有錢銀)。
架構
數(shù)據(jù)存儲和管理
對于GQueues來說,數(shù)據(jù)是核心 - 把數(shù)據(jù)保存到設備上然后同步到WEB端。Android和iOS有著完全不同的數(shù)據(jù)管理系統(tǒng)。 Android提供了ContentProvider,它是SQLite數(shù)據(jù)庫上層的一個可被繼承的應用接口,作為一個結構化框架被用于所有應用的數(shù)據(jù)處 理。ContentProvider學習起來比較難,搞定一個GQueues可用的實現(xiàn),前期需要花很多工作。一旦搞定了***步后面的擴展和個性化定制都 變得簡單多了。
一些背景信息,GQueues的web service是基于Google App Engine’s Datastore的, 這是一個高擴展性的分布式NoSQL存儲系統(tǒng),而SQLite則是一個標準的關系型數(shù)據(jù)庫,擴展性明顯也比較差,但這完全不需要考慮,因為這個應用只存儲 一個用戶的數(shù)據(jù)。(順便說一下,架構上我采用了“一個用戶對應一個數(shù)據(jù)庫”的設計,這對于快速簡單地實現(xiàn)多用戶切換有重要意義,不過實現(xiàn)細節(jié)可能得再開一 博來聊了)。不管怎么說,Android的一個很大的優(yōu)點就是可以創(chuàng)建SQLite Views來支持Smart Queues。為了支持Smart Queues,搞清楚各種復雜的表關聯(lián)查詢和子查詢也花了寫功夫,但是這也讓Smart Queues的加載更加高效和快速,因為過濾不是在代碼里面實現(xiàn)的(在SQL里面)。
在iOS上,我用的是Core Data, 它是iOS上的“schema驅動數(shù)據(jù)圖形管理和持久化框架”,基本上它可以被看做是一個NoSQL存儲,不過有趣的是,Core Data背后實際上是 SQLite數(shù)據(jù)庫(呃…實際上SQLite也是幾個可選項中最合理的選擇)。iOS也允許用戶直接創(chuàng)建SQLite數(shù)據(jù)庫,但只支持通過純C代碼來操 作,對于其他iOS組件沒有原生集成。Core Data的學習起來也比較困難,但***我還是選擇Core Data而不用SQLite,因為這樣我可以 輕松實現(xiàn)很多功能,包括緩存、數(shù)據(jù)模型遷移支持,還有通過 NSFetchedResultsController,可以非常簡單地為界面中的 table(列表)提供數(shù)據(jù)。
管理數(shù)據(jù)集的關鍵就是使用事務,尤其重要是做數(shù)據(jù)同步的時候 - ACID,即:atomic(原子性)、consistent(一致性)、 isolated(隔離性)、durable(持久性)。Android上實現(xiàn)事務似很直觀,跟大部分關系型數(shù)據(jù)庫管理系統(tǒng)的實現(xiàn)方式是一樣的,因此,保 證數(shù)據(jù)完整性并不困難。另外,用好SQLite中的UNIQUE ON CONFLICT REPLACE語句,在數(shù)據(jù)同步的過程中建表、對記錄進行原子更新的時候幾乎不需要做任何額外工作。
嚴格來講,Core Data并不完全支持事務。通過使用單獨的子ManagedObjectContexts做后臺線程處理,再加上@synchronized,能夠處理好數(shù)據(jù)更新和同步,同時避免不正確的寫操作覆蓋(overwrite)。關于高效更新和創(chuàng)建對象,iOS給的建議幫助很小,總的來說,CoreData給我的趕腳很笨重,并沒有它聲稱的那么好用。另外,在Android上,SQLite可以輕松實現(xiàn)快速加載Smart Queues,而在iOS上,所有的過濾都必須在代碼中實現(xiàn),就算用了大量的緩存,速度仍然很慢。
搜索
在GQueues for Android上增加強大的全文搜索功能很簡單,我模仿Google I/O應用里面的搜索實現(xiàn),使用了SQLite的FTS3特性。首先創(chuàng)建一個虛擬表,然后在一個存儲了用戶任務的表上設置幾個觸發(fā)器,由這些觸發(fā)器填充數(shù)據(jù)到虛擬表。做完了這些,剩下的就是設計一個搜索界面和為搜索歷史添加存儲。
iOS的Core Data對于全文搜索并沒有原生支持,所以我通過在謂詞(Predicate)中使用LIKE語句,實現(xiàn)基本的任務描述和日記的搜索功能。這個實現(xiàn)當然沒有全文搜索那么強大,但我認為它已經(jīng)能夠覆蓋現(xiàn)實生活中大部分的使用場景了。
特性API
用于比較,我只會列舉在GQueues中使用到的幾個API。
快速添加(Quick Add)
正則表達式在實現(xiàn)GQueues中Quick Add解 析的時候扮演著一個非常重要的角色,幸運的是,Android和iOS對于正則表達式都有著原生的支持。Android中的Pattern和 Matcher從***個版本起就開始支持,同時也包含了很多正則語法,其中包括前向斷言(look-ahead assertion)和后向斷言 (look-behind assertion)。iOS則從iOS 4開始引入NSRegularExpression類,令人高興的是,我可以把我在Android上辛辛苦苦寫好的正則表達式幾乎原封不動地搬到了iOS上。
分頁
在設計界面的時候,我希望用戶在查看任務詳細的時候左右滑動切換。在Android上我用了 ViewPager和新的FragmentStatePagerAdapter類,F(xiàn)ragmentStatePagerAdapter 還處于試驗階段,并且只能通過支持庫(Support Library)來使用。我花了幾天的時間實現(xiàn)了一個綁定好數(shù)據(jù)的初級版本,同時解決了幾個關于重 復菜單項的bug和在數(shù)據(jù)發(fā)生變化后的處理。這些比我預想的要困難很多,要不是因為左右滑動切換任務的用戶體驗那么好的話,我真不想實現(xiàn)這個功能。iOS 上的UIPageViewController就簡單很多了,雖然也有一些奇怪問題要解決,并且需要自己再加上緩存支持使滑動復雜視圖的時候達到可用狀態(tài)。
語音輸入
Android提供了了一個先進但很容易使用的speech-to-text API,只用20行代碼,我就把RecognizerIntent集成到GQueues,提供了一個自定義的語音輸入功能。但很遺憾,iOS并沒有提供支撐SIRI背后技術的API,開發(fā)者只能使用第三方庫,依賴鍵盤上的麥克風提供語音輸入的支持。我找了各種第三方庫,包括Nuance - SIRI語音識別的提供商,但發(fā)現(xiàn)沒有免費版本,收費版本價格不菲。所以***GQueues只能靠用戶自己使用鍵盤上內(nèi)置的麥克風選項來進行語音輸入,其實這也已經(jīng)足夠了,只要用戶還記得有這么個功能。
分享/插件(小部件)
通過使用Intent,在Android上可以很容易就可以把我的應用集成到安裝在用戶手機上的其他應用。同樣地,只需要很少的代碼,通過支持ACTION_SEND intent,我就能夠讓用戶在其他應用中創(chuàng)建GQueues任務。Android同時也提供了一個小部件平臺,于是我也做了幾個小部件,以后還會增加一些。iOS對于跨應用集成和桌面小部件的支持度為零,完全不支持這兩個功能。
測試和發(fā)布
beta測試
在上面的統(tǒng)計概況表中已經(jīng)指出,beta版面向真實用戶測試了一個多月。兩組測試人員都非常棒,幫我找到了數(shù)十計的bug,提出了增加一些特性的建 議,對一些UI上不合理的地方提出了反饋。我通過私有的Google Group組織beta測試,這樣的beta測試保證了***發(fā)布的應用對人們是真正 有用的。在每次beta測試的***,通過調(diào)查問卷我收集到了很多有建設性的反饋,也幫助進一步判斷我的應用是否達到了可發(fā)布的狀態(tài)。
讓測試者開始測試只需要發(fā)個APK的鏈接,讓他們下載到他們機器上(呃..他們還需要在設置界面中開啟“允許安裝Google Play以外的應用”的選項)。Google很方便地支持用真實用戶來進行alpha和beta測試,可在開發(fā)者控制臺和階段推廣中進行設置。在未來的版本更新中我想用用這兩個功能。
iOS中的beta測試困難得多,就算用了TestFlight服 務,雖然TestFlight很大程度地簡化了流程。為了滿足Apple的控制欲,每部測試設備的UUID都要加到用于簽名beta版應用的證書當中。因 此,每次要添加beta測試者的時候,不論是添加一個人還是一群人,我都需要重新build一遍我的app。除此之外,Apple還限制了你一年最多只能 注冊100個測試設備。所以我要小心利用好這100個坑,這也是為什么GQueues的iOS測試者只有Android的一半。
發(fā)布
當然,不談談發(fā)布流程,Android和iOS的比較都不算完,在Google Play上發(fā)布GQueues是一件很好玩的事情,只要我認為已經(jīng) 準備好了,我隨時可以發(fā)布我的應用。點下按鈕之后,30分鐘內(nèi),我的應用就能在Google Play上被全世界的用戶找到并安裝到他們的設備上。而在 App Store上發(fā)布一款應用,相信每個iOS開發(fā)者都有同樣的感受,那是一個令人感到郁悶的經(jīng)歷。經(jīng)過了數(shù)月緊張嚴密的編碼,我只能把我的創(chuàng)作提交 給Apple,然后等7天,7天之后審核人員花2分鐘看看我的應用,***拒絕了我的提交。我只能按要求做了修改之后再次提交,我又得等8天才在***通過了 審核。當然還有很多關于提交應用到App Store的恐怖故事,跟他們比起來,我就像是公園里逛了一圈。盡管如此,在自己的商業(yè)控制上要對這樣一個“情 緒化的第三方平臺”做出那么多的讓步,仍然讓我覺得很不爽。
獲勝的平臺
從上面的分析來看,做GQueues的過程中,并沒有出現(xiàn)平臺A完勝平臺B的情況。Android和iOS在某些領域各有千秋,也都有需要改進的地 方。從這兩個平臺的歷史來看,貌似目前Android勢頭更猛一些,不止體現(xiàn)在市場占有率上,而是看到了Android近兩年在UI上的改進和開發(fā)平臺的 穩(wěn)步提升。而Apple則是封閉的王者,我也堅信他們在很努力地做著他們認為是下一代移動計算革命的事情。不管怎么說,當我想想這6年間所興起的app生 態(tài)圈,我為自己在這個移動技術快速更新的時代,能在這兩個平臺上做開發(fā)感到榮幸。