愛奇藝App啟動優化實踐分享之安卓篇

愛奇藝技術產品團隊 發佈 2019-12-19T07:06:19+00:00

導讀性能優化一直都是各個APP推進中的重點、難點,愛奇藝 App也不例外。在此之前,愛奇藝App Android 版的啟動速度雖然一直處於同類App領先的水平,但優勢距離其他同類的APP距離一直很小。面對這一瓶頸,愛奇藝技術產品團隊為愛奇藝App的啟動優化搭建了專項團隊。

導讀

性能優化一直都是各個APP推進中的重點、難點,愛奇藝 App也不例外。在此之前,愛奇藝App Android 版的啟動速度雖然一直處於同類App領先的水平,但優勢距離其他同類的APP距離一直很小。面對這一瓶頸,愛奇藝技術產品團隊為愛奇藝App的啟動優化搭建了專項團隊。在各個團隊的大力支持下,將啟動時間由1.5s優化到了0.5s,實現了巨大跨越,下文將分享愛奇藝App啟動的優化實踐。

本文長度為2914字,預估閱讀時間8分鐘。

凡事預則立,不預則廢

關於App 的啟動優化, 大部分的技術同學第一反應是延遲執行任務,再通過做一些代碼實現上的優化來提高代碼的執行效率。愛奇藝App 早期的優化策略也是如此:將某些啟動階段不必要執行的任務延遲到首頁某個生命周期之後再執行,或是延遲固定時間之後再執行等等。

這一優化策略在前期的確有一些效果,但是後來就收益甚微了。愛奇藝App(Android)經過多年的穩定疊代, 功能不斷豐富,代碼量也越來越龐大。啟動階段涉及了很多業務的初始化功能,業務之間又有著複雜的依賴關係,輕易變更代碼執行時序可能會導致崩潰或者某些業務異常。之前簡單的延遲優化策略已經很難再取得收益,一種全新的優化方案已經迫在眉睫。

工欲善其事,必先利其器

有沒有一種工具能直接看到當前任務執行的瓶頸呢?做過Android性能優化的同學應該都會比較熟悉Systrace和 TraceView這兩款工具。但是這還不夠,我們需要一款更為直觀、便捷的工具。它能夠把啟動階段代碼執行的先後順序、時間間隔、線程情況以及代碼位置等信息直觀的展示出來,讓我們能一眼看出某一階段的任務調度是否合理,然後再根據實際情況進一步做有針對性的優化。

但到目前為止,我們尚未發現業內有滿足此需求的現成工具。然而在一年前,我們團隊自主開發了一款Lens開發調試工具,它能夠以SDK的形式接入任意的Android 應用程式,可以提供任務分析、網絡分析(抓包)、頁面分析(視圖拾取、視圖層級等)、沙盒訪問、快捷入口等等功能。於是我們團隊經過討論,決定用Lens來實現任務分析的功能,這樣既能完成啟動優化,又能進一步完善Lens的功能。經過我們團隊持續不斷的完善和優化,突破重重技術難關。終於實現了可以直接看見任務的執行狀況的能力,就像醫生拿到了病人的CT報告,精準優化已準備就緒。




診斷:啟動階段有大量的線程啟動,執行了大量的任務,並且主線程任務十分密集。

經過充分的思考後,我們團隊給出了如下的優化方案:

· 任務化:將啟動階段的代碼按照業務邏輯封裝成獨立任務,方便管理和調度。

· 並發:將啟動階段的任務儘量並發執行。

· 延遲:啟動階段只執行第一個頁面渲染展示的必要任務,延遲的任務將在第一個頁面渲染完成後再進行合理的調度觸發。

· 兜底:設計兜底機制,保證程序穩定執行。

· 監控與優化:建立常態化監控機制,監測任務變化情況,實現精準優化。

由於啟動階段的任務之間存在相互依賴關係,被依賴任務會出現異步執行未完成而導致的崩潰問題。為了解決這一問題,一種基於依賴關係來動態調度任務執行的任務管理器——《TaskManger》被設計出來。

TaskManager(後文簡稱TM)支持給任務設置執行條件,例如等待某個任務執行完成後或者某個事件發生後再開始執行;支持「關係與」依賴、「關係或」依賴,支持延時執行,支持把任務提交到主線程或者子線程執行等等。除此之外,任務之間還能傳遞數據,監聽事件等等。



一、任務化

我們重新梳理了Application 階段的代碼,並按照功能業務歸類整理,提煉出了多個任務模塊。例如,我們將播放器部分的初始化工作封裝在PlayerInitTask類中。每個任務類都分為任務註冊與任務執行兩個部分,依據程序是否會啟動頁面展示,我們設置了不同的任務執行策略。

PS:靈活的任務執行時機設置也是TM的一大優點。



二、並發與線程收斂

在完成啟動業務的梳理和重構之後,將可以在子線程中運行的任務提交到子線程中執行,將之前比較耗時的任務分拆為多個子任務,阻塞並發執行,從而儘可能的充分利用CPU的算力。

並且將啟動階段的任務都對接到了TM統一調度,避免其它線程搶占CPU資源以及一些不必要的系統開銷問題,完成了啟動階段線程的收斂。通過採取這些措施啟動階段的線程數明顯降低,主線程因為子線程搶占CPU資源而導致的執行效率過低的問題也得到了解決。


三、延遲

在啟動階段主要設置了兩個事件:廣告展示事件和首頁展示事件。按照業務執行的必要性,我們把啟動階段不必要執行的任務分別延遲到了這兩個階段之後執行。任務之間也按照之前梳理的依賴關係,分別設置了相應的依賴條件,解決了因被依賴任務被延遲未執行而導致的崩潰或業務異常問題。TM還 提供了一些運行監測機制,能有效避免循環依賴問題的發生。

由於TM的調度管理,這些任務在運行條件得到滿足後會根據當前運行條件與設置的任務優先級等情況合理調度執行,不會出現大量任務同時並發執,從而導致頁面卡頓的問題;

四、兜底

當任務延遲後,任務執行時機存在不確定性。這可能導致在業務需要時,一些初始化任務還沒有執行完成的問題——TM 提供了任務兜底機制。

使用上面的API可以保證業務依賴的任務一定被執行,從而解決了因為「異步」與「延遲」可能給業務帶來崩潰或者邏輯異常問題。

五、監控與優化

1)阻塞等待監控

由於TM提供了任務的兜底機制,理論上就會存在因業務需要而導致等待任務執行完成的場景產生。這種問題在低端設備上更為常見。為了解決這一問題,我們在TM中添加了任務等待監控功能,將其納入常態化的監控中,並且成功發現了多項阻塞等待的問題。通過通知業務方進行邏輯優化,大大減少甚至避免了部分阻塞等待的問題,用戶體驗獲得了更進一步的提升。

2)啟動數據監控

我們將Lens的啟動數據投遞到後端,並且建立了啟動分析日報機制。將啟動過程分段,除了啟動時間外,還投遞了程序初始化階段、頁面創建階段、頁面渲染階段的分階段耗時數據,當某項數據明顯劣化後,服務端就會發出預警。

3)歷史版本啟動任務對比分析

目前最新的Lens 版本支持版本間啟動任務對比分析功能,我們可以在介面上展示出兩個版本之間的任務變化差異,例如新增任務、減少任務及運行耗時變化超出閾值的任務信息。通過分析這些變化的任務信息,就可以直接「約談」相關業務方,進而快速的完成優化工作。



與此同時,我們團隊還執行了一些其他的優化手段,例如推進代碼執行效率的優化、提前加載一些重要展示數據等等。

總結

通過多個版本的疊代,完善了任務調度框架、啟動任務常態化監控機制、任務分析功能等等。



現已經優化到500ms以內,啟動任務、啟動階段線程數也大幅度減少。在這次的優化實踐工作中,Lens 與TM 兩個工具發揮了重要作用。關於Lens的細節,我們將在後續進一步為大家分享。

關鍵字: