愛奇藝APP Android低端機性能優化

01

   背景介紹

在智能手機市場上,高端機型經常備受矚目,但低端機型亦佔據了不可忽視的份額。衆多廠商爲滿足低端市場的需求,不斷推出低配系列手機。另外過去幾年的中高端機型,隨着系統硬件的快速迭代,現已經被歸類爲低端機型。愛奇藝APP擁有龐大的用戶基羣,其中低端機型用戶也佔據了相當一部分。低端機優化能給這部分用戶帶來穩定、流暢、高效的使用體驗。下面將從冷啓動、流暢性、加載速度三個維度介紹愛奇藝APP對低端機的優化策略。


低端機分級策略
介紹優化之前,我們看看低端機的標準,低端機設備的判斷通常基於設備型號、內存大小和系統版本等因素。愛奇藝APP有自己的低端機分級策略,可在策略後臺分場景(啓動、流暢度等)、分等級(內存、機型、系統等)配置優化策略,以保障不同場景的最佳體驗。

02

   啓動優化

啓動是APP給用戶打開的第一扇門,它的耗時長短直接影響用戶後續的觀感體驗和留存,對業務指標有明顯的影響。因此啓動優化是技術優化方向的重點工作內容。

相關介紹

啓動階段的起點和終點:愛奇藝APP以 Application.attachBaseContext 爲起點;以首頁數據展現終點,該階段經歷的時長,作爲線上正常冷啓動耗時。這個主要經歷 Application 創建階段,MainActivity創建顯示階段,廣告,首頁頂底導航數據加載及渲染,首頁數據加載及渲染這幾個階段。
在這個過程中,要梳理出業務層做了哪些工作,評估其執行的必要性和執行時機以及所處調度線程的合理性;要監測主線程狀態,是否陷入長時間休眠;主線程消息/後臺消息監測治理,是否有不符合預期的任務被觸發;是否有充分利用系統資源,是否充分利用空閒時機做預加載等。
業務功能原子化:爲將啓動階段的任務能夠有序調度、合理分配資源,開發了一套對任務管理的框架TaskManager,將業務功能實現包裝在自定義Task裏面,拆分得足夠細,並設定Task之間的執行依賴關係,預期被調度執行的線程,執行時機等,然後統一交由 TaskManager處理,這層任務管理是我們實施啓動優化的基礎。


優化實踐

開屏首頁合併

早期愛奇藝APP開屏和首頁是兩個activity,兩個activity帶來一些用戶體驗問題:開屏結束剛進首頁時低端機卡頓明顯,首頁也沒有立即展現,用戶能看到首頁數據和圖片都有一個從無到有的加載過程。
開屏階段大多數場景是有開屏廣告的,將開屏和首頁合併到一個activity,利用開屏廣告階段將首頁放到開屏頁面的下面加載,並且將首頁數據和UI展現分離並行處理,最大利用該階段,最終可達到開機屏結束時首頁立即展現,低端機上卡頓也明顯好轉
首頁的渲染給開屏廣告也帶來的一定的影響。廣告倒計時顯示不穩定、少數類型的廣告效果不流暢。對此首頁加載拆成很多步驟在廣告的回調的觸發解決,倒計時顯示使用surfaceView渲染保障其穩定性。


任務調度優化

基於啓動類型,編排啓動階段任務執行時序,提前,延後,或不執行,從而讓用戶儘早的看到目標頁面。以下是愛奇藝針對低端機正常啓動路徑下,對任務做出的調整。


解決鎖競爭

native庫加載鎖競爭:C層庫加載是有鎖的,Java層開多線程加載Lib庫,到C層後仍會順行執行加載,這就會導致Java層線程阻塞等待。愛奇藝在Application階段有Lib庫加載的需要,並要在主線程等待其加載完成後調用相關的JNI方法;而又遇到播放模塊針對外部拉起進播放頁這種情況,有預加載播放相關Lib庫的需要,這會導致主線程進入等待狀態。可通過在啓動階段識別目標落地頁,來決定是否執行播放相關的Lib庫預加載,從而避開大部分正常啓動進首頁的用戶卡頓。(測試機 紅米K40,Android12系統)

資源鎖競爭:愛奇藝首頁展現前,有別的模塊在子線程預加載佈局文件,導致 LayoutInfalter / ResourceManager / AssertsManager 層鎖競爭激烈。將預加載佈局的任務調度到首頁展現後執行,並且限制預加載佈局在同一個子線程裏面執行,可以看到改進後鎖衝突次數大大減少了,這樣能讓首頁更快展現出來。


Baseline Profiles

Baseline Profiles:Google在2022年推出Baseline Profiles,允許開發者在apk內置自定義的熱點代碼基準配置文件。在APP安裝期間,系統通過配置文件提前預編譯熱點代碼。可以略過運行時所包含代碼路徑的解譯和即時編譯步驟,提升首次啓動代碼執行速度。
Startup Profiles:啓動配置文件是上述基準配置文件一個子集,使用啓動配置文件可改進APK的DEX文件中的代碼佈局,從而進一步優化包含的類和方法。愛奇藝APP通過啓動配置文件將啓動階段的代碼構建到同一個DEX文件中。使用上述兩個策略愛奇藝APP在部分機型上首次啓動速度提升約10%。



外鏈拉啓優化

外鏈拉起也是一種重要啓動方式,通常有H5、分享、第三方APP等方式拉起,與正常冷啓的區分在於外鏈拉起的往往不是首頁,是特定的目標頁。愛奇藝最常見的場景是拉起播放頁,如果我們提前(application階段)識別落地頁,可以針對該落地頁調整任務優先級,外鏈拉起播放頁我們提前識別出播放頁將播放器相關的任務提前初始化。通過這一策略低端機上外鏈拉起開播提速約1.5秒。


03

   流暢度優化

相關介紹
愛奇藝APP大部分頁面都是基於自研的Card框架進行開發。Card框架是一種高度可複用的UI框架,在使用原生代碼實現基礎UI佈局和業務邏輯的基礎上,通過後端下發的CSS控制來控制基礎容器樣式,來達到頁面區塊整體的複用,以及針對內容樣式動態微調。是我們雙端(Android和IOS)實現頁面整體複用和局部微調的一種解決方案。在基於這套框架的基礎上對APP內頁面流暢度進行優化,Card框架如下特點:
  • 複用度高:由控件構成內容區塊(Block),由多個區塊組成行;由多個行組合成Card,再由多個Card組合成整個列表頁面。其中業務最小可複用單元爲Block。
  • 動態性強:支持在後臺配置CSS文件,動態修改某個UI的樣式(文字的大小、顏色,圓角等等)。

優化實踐

樣式Native化

Card頁面的動態性和複用性導致了UI在佈局上覆雜性。一種類型的Block需要兼容多種樣式形態,例如一張圖上的四個角落,需要預埋各種類型的角標邏輯,而角標類型又包括純圖、純文、圖+文、可選中等形態。實現各種樣式會導致View個數多、嵌套層級深,使得某些頁面在低端機上滑動不夠流暢。
爲了優化這一現狀,篩選出一些業務形態穩定的Card,針對這類Card做樣式固化,在佈局上做了大量精簡。使得上下滑動上帶來了明顯的幀率提升。例如瀑布流Card,我們將實現的View數量由40+減少到17個,佈局層級從6層下降低到2層;在不同的低端機上,帶來了10%到20%不等的滑動幀率提升

View合併繪製
上述佈局簡化策略帶來的提升效果是明顯的,但由於業務形態多樣化,使得一些必要的View無法刪減。爲了進一步減少View個數和層級,將常用的Block佈局中多個view合併到一個自定義的View上,利用View的畫布,繪製文本、圖片、按鈕等樣式信息。此方法可以有效減少View個數和嵌套層級,但仍需處理每個元素點擊事件和按壓效果。低端機,此種策略可以帶來約1~2fps的滑動幀率提升


預創建&異步加載

佈局預創建:如上圖介紹的三種樣式的Card中,使用的都是block類型相同(上圖下文)。我們將這些常用的、複用度高的Block佈局,在啓動階段預加載到緩存池中,使得列表滑動中可以直接使用預創建的佈局,從而減少UI繪製中的inflate時間。
佈局異步創建:預創建對常用的佈局效果是不錯的,但不常用佈局還是佔據多數,這類佈局在滾動過程使用AsyncLayoutInflater異步創建將要出現的佈局,減少滾佈局在UI線程的創建時間,同時也可以提升RecyclerView預取的效率。
RecyclerView預取:愛奇藝APP不少頁面用嵌套的RecyclerView去做橫滑滾動產品形態,這類形態的card大多數在一屏裏會露出3-5個item,根據不同形態設置不同的預取數(setInitialPrefetchItemCount,默認是2)可以減少這類card露出時的卡頓。
主線程減少非UI任務執行:在滾動過程檢測主線程上耗時,發現部分非繪製任務在主線程執行,UI線程解析JSON,建立數據庫鏈接等放到異步任務執行。


冷啓UI消息調度

低端機在冷啓動的過程中,資源消耗會逐步達到一個峯值狀態;存在大量的UI消息(需要在UI線程執行)和其他後臺任務需要執行。通過攔截埋點分析,這階段有4000多條消息(15秒內)需要在UI線程上執行。在低端機上這些消息執行的時間從1ms到150ms不等。執行這些消息時系統的UI渲染消息就會被延時執行,在低端機上用戶在APP剛啓動時面臨滑動卡頓、點擊響應慢等問題。
針對這一問題,我們的解決方案是攔截所有往UI線程發送的消息加入到自定義的消息隊列中;再監測系統UI消息隊列的是否空閒狀態,空閒時從自定義隊列取出消息重定向到系統UI消息隊列執行;另外增加白名單機制針對部分高優的消息放行。針對異常有回退機制處理。
通過對UI消息調度,使得低端機在冷啓階段卡頓程度得到了明顯的改善;通過對線上大數據監測,凍幀和掉幀數大幅度減少。冷啓階段提升幀率提升約8fps


效果降級策略

低端機上通過對部分效果降級有效的減少特定場景下卡頓, 愛奇藝APP做了如下的降級策略。
動效降級:頂底導航動效降級到靜態圖、播控動效關閉、部分產品功能的動效簡化處理等。
播放降級:滾動延時播放&部分場景不開播等策略。
ViewPager預加載降級:禁止左右Tab的預加載,減少了view繪製時間和內存開銷。
圖片降級:部分頁面動圖不播,圖片使用565像素格式。


04

   加載速度優化

相關介紹
除了前述的啓動優化外,我們還專注於優化一些重要頁面,因爲這些頁面的用戶訪問頻率極高,對它們的優化能夠顯著提升用戶體驗。舉例來說,搜索是用戶經常使用的功能之一,因此我們對搜索行爲進行了精細化拆解,並對搜索的每個步驟都進行了優化處理。

優化實踐

預請求

通常,頁面的渲染過程通常從Activity的onCreate方法開始。之後再發起網絡請求以獲取必要的數據。獲取到具體的數據之後再進行頁面的渲染,之前在搜索場景我們也是這樣的流程。
那有沒有可能提前獲取到需要的數據呢?
其實用戶在首頁點擊搜索框時,已經有了網絡請求所需要的參數。那就完全可以在點擊時,提前發起網絡請求,網絡請求與頁面跳轉併發進行,縮短了網絡請求時間。如果機器性能越差,頁面點擊到onCreate方法的時間越長,優化的時間也越多。低端機驗證減少耗時約200ms。


據分次下發

用戶在進入頁面時,僅會呈現首屏數據,而頁面下方的數據則需待用戶執行滑動操作方能顯示。因此,我們優先確保首屏數據的展示,通過減少首次數據下發的大小,減少了數據獲取、數據傳輸、數據解析所需的耗時。在首屏數據渲染完成後,我們會再次發起接口請求,確保用戶在執行滑動操作時,也能夠立即呈現後續數據。在首頁、半播頁、搜索等多個重點頁面都採用該方案進行優化,比如在搜索場景使用該方案,驗證減少約200ms。


預創建

佈局預創建:在搜索中間頁空閒時,我們針對複用程度很高的佈局,提前將這些佈局創建到緩存中,頁面真正渲染時,直接使用預創建好的佈局,避免了view進行inflate的時間。
Fragment 預創建:在搜索中間頁空閒時,提前創建好結果頁的Fragment容器,在結果頁展示時就不用創建對應的容器,減少容器創建耗時。



主線程優化

在頁面加載時,希望儘可能頁面相關的主線程任務先執行,如果重要任務調度被搶佔了,就會影響頁面渲染效果。通過我們內部自研的Tracepeed工具,接管主線程的Looper.loop() ,可以發現主線程中耗時較大的任務。如果發現耗時較長的低優先級任務被先執行了,可以調整任務調度,讓重要任務優先執行。
比如在搜索結果頁加載過程中,圖片加載是一個重要的任務。但在低端機上,發現某些情況下會有另外一個任務搶佔主線程,導致圖片加載耗時有時長達1s。針對這種場景,調整任務調度,讓圖片加載任務優先執行,耗時穩定到在100+ms。

業務邏輯優化

針對不同的業務,我們也分析了具體的業務邏輯,對相關邏輯進行優化:
  • 空圖優化:在選集加載時,某些場景,後端會下發一個空圖片,而這個空圖片也會進行加載,導致頁面加載耗時增加,因此我們針對空圖,限制不進行加載。
  • 高頻邏輯優化:在進行優化時,高頻方法是一個需要重點關注的點,優化一些常用的高頻方法,各個頁面耗時都可以得到優化。比如:在基礎控件上的初始化上,避免無用方法的執行,減少了這類高頻方法的耗時。
  • 耗時方法異步執行:在頁面加載過程中,也會有一些優先級不那麼高的業務邏輯,當發現這些邏輯後,可以通過異步框架去執行這些邏輯,從而減少頁面加載耗時。


防劣化

經過持續不斷的優化,頁面加載耗時已達到穩定水平。然而,在不斷進行的開發迭代過程中,我們注意到了一些耗時增加的情況。如何有效地防止這種劣化的發生呢?
通過在代碼裏面重點方法進行埋點,每天定時執行流水線任務,多次執行取平均值後。用可視化的方式可以發現頁面加載的波動情況。如果出現了劣化,就可以通過任務對比,直觀的發現兩次任務耗時差異點在哪裏,針對耗時差異點進行分析優化。


05

  總結與展望

低端機優化包括很多方面,上述介紹的幾個核心業務場景下的部分優化手段,優先處理關鍵性能問題,有效提升低端機的運行性能,其中需要用的工具分析、線上監控、衡量標準這裏沒有提及,這些也是性能優化的重要利器。Android碎片化嚴重,低端機優化任重而道遠。後續我們繼續精進打磨,尋找新的優化突破點,以技術創新爲用戶提供穩定流暢的使用體驗,助力高質量增長。


本文分享自微信公衆號 - 愛奇藝技術產品團隊(iQIYI-TP)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章