? ? ? ?我們向全球最流行的一些 App 的開(kāi)發(fā)團隊(包括微信,雅虎新聞?wù)┤〗?jīng)了開(kāi)發(fā)流暢 App 的最佳實(shí)踐經(jīng)驗。通過(guò)我們自己的經(jīng)驗以及與這些頂尖開(kāi)發(fā)者的交流,我們發(fā)現開(kāi)發(fā)優(yōu)秀 App 最重要的實(shí)踐經(jīng)驗是建立一個(gè)性能優(yōu)化的流程:如何讓你的App更流暢優(yōu)化過(guò)程? ? ? ?當你的 App 性能不佳時(shí),持續測量 App 的性能表現就變得尤為重要。一旦檢測出性能問(wèn)題,你應專(zhuān)注于對問(wèn)題尋根溯源。診斷可能還會(huì )用到更多的測量和詳細的性能數據,使你一段時(shí)間內陷入我們稱(chēng)之為“測量-分析循環(huán)”的怪圈。即使在找到問(wèn)題的根本原因后,光修復爛代碼還不夠。你還得重新驗證各項指標來(lái)確定修復是有效的。這就意味著(zhù)更多的測量。測量? ? ? ?有兩個(gè)最為影響用戶(hù)體驗 (UX) 的性能指標。第一,我們要注意的是響應時(shí)間:你的 App 要花多久時(shí)間來(lái)響應用戶(hù)的操作 (比如啟動(dòng) App, 瀏覽新文章, 加載聯(lián)系人名單, 或者打開(kāi) Facebook 頁(yè)面)。假如你的 App 在所有上述情景中都能迅速響應,這將幫助你建立一個(gè)更好更完美的用戶(hù)體驗。? ? ? ?一個(gè)關(guān)鍵(而獨特)的響應時(shí)間的例子是啟動(dòng)時(shí)間。App 的啟動(dòng)是裝了這個(gè) App 的用戶(hù)的第一次用戶(hù)體驗, 而第一印象又是最重要的。事實(shí)是,有關(guān)于軟件的研究發(fā)現79%的用戶(hù)在裝了有啟動(dòng)問(wèn)題的 App 后只重試了一兩次就卸載了。以下是 我們給出的一些建議,我們在軟件性能及優(yōu)化領(lǐng)域有著(zhù)多年經(jīng)驗。? ? ? ?我們建議不要讓你的 App 花在啟動(dòng)上的時(shí)間超過(guò)兩秒,因為這是用戶(hù)期待中 App 啟動(dòng)的平均時(shí)間。熟悉網(wǎng)頁(yè)性能的都知道,47%的用戶(hù)希望頁(yè)面能在兩秒以?xún)燃虞d完畢,而用戶(hù)們在外趕時(shí)間時(shí)花在移動(dòng)端 App 上的耐心就更少了。建議一:兩秒內使 App 啟動(dòng)? ? ? ?第二個(gè)重要指標是流暢度。即使一個(gè) App 總體上來(lái)看響應時(shí)間很短,響應本身必須也要流暢,使“卡頓”盡可能少。用戶(hù)對時(shí)間上的卡頓非常敏感,意味著(zhù)即使是小小的頓挫也會(huì )影響用戶(hù)體驗。平均來(lái)說(shuō),人眼能分辨小到 22 毫秒的卡頓,而四分之一的人群能察覺(jué) 2 毫秒到 16 毫秒的卡頓 – 60幀每秒 (FPS) 的刷新率就是根據這個(gè)原理。? ? ? ?你可以從你的 App 的 FPS 和幀時(shí)間數據得出 App 的流暢度。然而請注意這數據不會(huì )告訴你你的 App 的性能問(wèn)題根源所在,這就意味著(zhù)它沒(méi)法幫你找到 App 卡頓的 method。? ? ? ?在安卓系統中,用戶(hù)界面 (UI) 線(xiàn)程 (你的 App 執行的主線(xiàn)程) 是唯一能對用戶(hù)界面進(jìn)行更新的線(xiàn)程。要保持一個(gè) 60 FPS 的刷新率,UI線(xiàn)程必須在每 16 毫秒內完成一幀的繪制。如果在 UI 線(xiàn)程上的任何 method 的調用時(shí)間超出了這一時(shí)間, 你的 App 就會(huì )掉幀, 產(chǎn)生時(shí)間上的卡頓。更糟的是此時(shí)你的 App 對任何的用戶(hù)操作都無(wú)響應,因為 UI 線(xiàn)程還在被這個(gè) method 調用所占據。? ? ? ?實(shí)際上,要確保 UI 線(xiàn)程的每一個(gè) method 的調用時(shí)間都少于16毫秒是幾乎不可能的。32 毫秒的門(mén)檻值,相當于兩個(gè)幀的長(cháng)度,才更合理。我們把超出這個(gè)門(mén)檻值 (執行時(shí)間超出32毫秒) 的 methods 叫做掛起 methods,因為它們使得 App 看上去“掛起”了。為了使你的 App 絲般順滑,收獲更佳的用戶(hù)體驗, 消除所有的掛起 methods 將大有裨益。建議二:消除掛起 methods? ? ? ?好 吧。既然測量同用戶(hù)體驗相關(guān)的指標是如此重要,那么我們該多久測量一次指標呢?每個(gè)版本?每個(gè)日版本?在每次發(fā)布之前?在生產(chǎn)中?!你應當一有機會(huì )就進(jìn)行測量 —— 測量的頻率越高,你就能越早發(fā)現和補救性能問(wèn)題。同我們交流的 Yahoo 團隊在每次發(fā)布前對 App 的性能進(jìn)行采樣,而微信團隊對每個(gè)日版本進(jìn)行性能采樣。建議三:盡可能多測量分析? ? ? ?優(yōu)化你的軟件的關(guān)鍵是找到常規的性能問(wèn)題并系統地從你的代碼中剔除。在對有著(zhù)超過(guò)五百萬(wàn)次下載記錄的 App 的性能問(wèn)題分析中,我們觀(guān)察到開(kāi)發(fā)者經(jīng)常使用在桌面端高效卻在移動(dòng)端低效的代碼結構。舉個(gè)例子,在一臺 MacBook Air 上一個(gè) JAR 文件調用methodClassLoader.getResourceAsStream 處理3K 資源的花銷(xiāo)約為 7 毫秒, 而在一臺 2013 年的 Nexus 7 上一個(gè) APK 文件調用這個(gè) method 同樣處理3K 資源的花銷(xiāo)約為 1700 毫秒。后來(lái)發(fā)現,安卓對于getResourceAsStream 的執行是在第一次調用時(shí)做了大量的額外工作,如為 APK 文件里的所有資源編排索引,驗證 APK 文件的證書(shū), 并解析其 manifest。這種操作是相當耗費 CPU 資源的,從而導致 App 的嚴重時(shí)滯 ——getResourceAsStream 給 Walgreens App 造成了 1.7 秒的時(shí)滯。建議四:了解一些通病? ? ? ?有時(shí)性能問(wèn)題是由你使用的第三方 SDKs 引起的,而并非你的代碼。這些問(wèn)題就很難被發(fā)現。譬如說(shuō)org.joda.time ,Java中一個(gè)流行的時(shí)間庫。你可能在以往的Java項目上使用過(guò)它。結果僅僅是創(chuàng )建一個(gè) org.joda.time.DateTime 對象就造成了巨大的時(shí)滯 —— Yahoo Fantasy Sports App 性能被它拖慢了兩秒之多。這是因為org.joda.time.DateTime 使用getResourceAsStream 來(lái)從 APK 文件加載時(shí)區數據。建議五:避免第三方 SDKs 帶來(lái)的麻煩優(yōu)化? ? ? ?修復爛代碼可能成為噩夢(mèng)般的過(guò)程。要從上百個(gè)進(jìn)程中找出拖慢 App 的進(jìn)程,這種尋根溯源的任務(wù)可能會(huì )占據數周的開(kāi)發(fā)周期。盡管如此,還是有一些通用的指導方針的。使用更有效的數據表現方式,算法,和執行方式或(萬(wàn)一你使用的是SDK從而無(wú)法直接修復代碼的話(huà))在后臺線(xiàn)程調用代碼而避免掛起在 UI 線(xiàn)程發(fā)生,這里面任意一種方式,都能使你的代碼運行得更快。遵循這些指導方針使 App 更有效率,大大有益于創(chuàng )造出人見(jiàn)人愛(ài)的產(chǎn)品。去哪尋求幫助? ? ? ?盡管性能優(yōu)化過(guò)程幫助你的 App 運行順暢,它也需要花費時(shí)間和一點(diǎn)點(diǎn)魔力來(lái)實(shí)施。這正是為什么我們要開(kāi)發(fā)安卓快速開(kāi)發(fā),強力優(yōu)化以及性能采樣工具的原因,這樣你們就能不分心地專(zhuān)注于你們的強項:為用戶(hù)帶來(lái)了不起的產(chǎn)品。