本文來自QCon全球軟件開發(fā)大會王勁鵬的技術(shù)分享,下文進行了排版優(yōu)化和修訂。
1、引言
性能和體驗在 iOS / Android 雙端場景下已經(jīng)是一個較為成熟的話題,但隨著鴻蒙 OS 的發(fā)展,端側(cè)開發(fā)者需要更多的關(guān)注多端場景的差異性。
本次分享的主題是小紅書在鴻蒙平臺上的工程實踐,主要聚焦于性能優(yōu)化和探索。(* PPT講稿原文下載:《小紅書鴻蒙OS下的性能優(yōu)化探索與實踐(PPT)[附件下載]》)
先介紹一下自己的背景。之前一直從事大前端領(lǐng)域的工作,主要專注于跨端和容器化方案。也曾手寫過一個跨端框架,名為 Doric,它可以對標(biāo) React Native、Vue Native 和 Flutter 等。Doric 框架在落地時表現(xiàn)良好,還支持了一些自研的 3D 引擎方案。除此之外,我還有播放器內(nèi)核研發(fā)經(jīng)驗,以及大前端常規(guī)體系建設(shè)和 CI/CD 流水線的工程經(jīng)驗。未來,我將持續(xù)關(guān)注大前端的演進,尤其是鴻蒙這樣的多端和跨端平臺。
從 2023 年開始,鴻蒙的優(yōu)勢愈發(fā)明顯,已經(jīng)成為可與 iOS、安卓媲美的第三大移動操作系統(tǒng)。從一些抖音視頻中也可以看出,鴻蒙在流暢性方面甚至在某些層面上超過了 iOS。
今天的分享內(nèi)容分為四個部分:
- 1)介紹整個歷程和背景;
- 2)介紹鴻蒙 OS 的相關(guān)能力和小紅書在該平臺上的優(yōu)化實踐;
- 3)通過鴻蒙 OS 提供的性能驗證工具,展示小紅書在鴻蒙平臺上的性能優(yōu)化驗證方法、優(yōu)化后的性能提升以及具體的收益和結(jié)果;
- 4)總結(jié)和展望。
2、內(nèi)容分享和整理
分享者:王勁鵬,內(nèi)容審校和編輯:Kitty。

王勁鵬:小紅書鴻蒙工程師。目前主要負責(zé)小紅書鴻蒙版的研發(fā)和工程建設(shè),曾從事過大前端架構(gòu)設(shè)計、研發(fā)效能等方向的工作,在終端架構(gòu)演進、性能優(yōu)化以及跨端容器和動態(tài)化等方面具備長期實踐及深厚經(jīng)驗,持續(xù)關(guān)注大前端技術(shù)體系,鴻蒙以及多端的演進。
3、版本歷程和開發(fā)背景
3.1 小紅書迭代歷程
從 2023 年年中開始,鴻蒙的“千帆計劃”正式啟動,并很快升級為“鴻飛計劃”。小紅書作為 7 家頭部合作商之一,率先支持了鴻蒙,并于 2023 年 11 月中旬上線了一個基礎(chǔ)版的 beta 版本 APP。這個版本主要包含筆記瀏覽和視頻筆記瀏覽兩大功能,以及一些簡單的個人設(shè)置。當(dāng)時,小紅書的動作非常迅速,可以說是頭部應(yīng)用廠商中對華為支持最為積極的品牌之一。
在整個鴻飛計劃中,我們規(guī)劃了三個核心里程碑:除了 2023 年 11 月的 beta 版本外,還包括 2024 年 6 月的 HDC 版本和 2024 年 9 月的商用版本。HDC 版本主要是針對華為正式宣發(fā)鴻蒙 3(HarmonyOS Next)開發(fā)者測試的情況。在 HDC 版本中,我們上線了許多小紅書特有的存量功能,包括視頻拍攝、圖文拍攝以及多設(shè)備協(xié)同等創(chuàng)新特性。而到了 2024 年 9 月的商用版本交付時,小紅書的核心功能已經(jīng)基本與主端對齊。考慮到鴻蒙的開發(fā)周期僅有一年,小紅書的鴻蒙 APP 在這一年中要對齊開發(fā)了十年甚至十幾年的安卓和 iOS 版本,難度和壓力都非常巨大。
到 2024 年 9 月,除了對齊雙端的所有功能外,我們還開發(fā)了許多其他功能,包括華為支持的創(chuàng)新特性,例如智能拖拽——用戶可以將圖片拖拽到中轉(zhuǎn)站或小藝等場景。此外,商用版本還支持了用戶呼聲較高的 HDR 或 Moonlight Photo 拍攝能力。
3.2 純血鴻蒙與安卓的區(qū)別
我從幾個維度來對比一下純血鴻蒙和安卓 OS 的主要區(qū)別。
內(nèi)核架構(gòu)純血鴻蒙的本質(zhì)是微內(nèi)核,而安卓是基于 Linux 宏內(nèi)核。微內(nèi)核只提供基礎(chǔ)的內(nèi)存和文件管理能力,驅(qū)動和其他系統(tǒng)能力都在 OS 之外。這樣做的好處是系統(tǒng)穩(wěn)定性極高,即使應(yīng)用崩潰,也不會導(dǎo)致整個系統(tǒng)崩潰(system crash)。而在 Linux 宏內(nèi)核中,應(yīng)用的不當(dāng)行為可能會直接導(dǎo)致系統(tǒng)崩潰。
多設(shè)備適配鴻蒙目前支持多種設(shè)備類型:包括 Mate 60 Pro 這樣的直板手機、Mate X5 或非凡大師 XT 這樣的雙折疊和三折疊手機、平板電腦、車機,甚至華為正在研發(fā)的鴻蒙 PC。鴻蒙真正實現(xiàn)了類似 iOS 的多端整合能力,通過一套代碼實現(xiàn)多端部署。其工程體系和架構(gòu)支持單 HAP(Harmony Ability Package)多 HSP(Harmony Service Package)模塊,指令集適配了 ARM64 等多種架構(gòu),開發(fā)者只需根據(jù)設(shè)備尺寸適配 UI 展示即可。例如,在 2024 年 9 月 的華為全場景設(shè)備發(fā)布會上,余承東展示了小紅書在從直板機到雙折疊、三折疊設(shè)備上的適配能力,完全實現(xiàn)了響應(yīng)式編程,不同設(shè)備形態(tài)下有不同的瀏覽體驗。
開發(fā)工具和編程模型鴻蒙的開發(fā)工具和編程模型與安卓差異較大。鴻蒙更類似于 Flutter 的嵌套型容器布局,而不是安卓那種面向?qū)ο蟮拈_發(fā)方式。在語言層面,鴻蒙完全封裝了底層邏輯,采用類似前端 Flux 單向數(shù)據(jù)流模式,通過數(shù)據(jù)變更驅(qū)動 UI 刷新。這種模式類似于前端 Redux 或 MobX 框架中的 state 管理 。
從 2024 年 10 月 8 日公測開始,鴻蒙的應(yīng)用生態(tài)正在逐漸繁榮。不過,目前像微信這樣的應(yīng)用還處于搶先體驗階段。相比之下,安卓的生態(tài)已經(jīng)相對成熟。鴻蒙的最終目標(biāo)是打造全場景智能設(shè)備生態(tài),涵蓋所有終端設(shè)備,以及基于 OpenHarmony 內(nèi)核開發(fā)的物聯(lián)網(wǎng)終端。它還支持多種芯片體系,例如瑞芯微 RK3568 等。
3.3 小紅書鴻蒙應(yīng)用架構(gòu)層級
小紅書經(jīng)過一年的迭代,其整體應(yīng)用架構(gòu)已經(jīng)基本成熟。目前,整體代碼量接近 200 萬行,達到了一個較高的復(fù)雜度。在一般成熟的 APP 架構(gòu)中,通常會包含一些基礎(chǔ)底層能力,例如網(wǎng)絡(luò)、磁盤存儲、埋點體系、APM(應(yīng)用性能管理)系統(tǒng),以及一些通用組件和能力。對于鴻蒙平臺,小紅書還具備一些特殊的公共通用能力。
我們開發(fā)了一個“一多框架”,這是一個支持一套代碼多端部署的具體框架體系。通過這個框架,我們實現(xiàn)了多設(shè)備的斷點控制功能。用戶可以根據(jù)設(shè)備的尺寸和類型進行適配,因為華為設(shè)備支持多端投屏。例如,用戶可以在手機上瀏覽小紅書,然后將內(nèi)容投屏到車機上。比如用戶購買了一輛問界汽車,可以在車內(nèi)通過車機繼續(xù)瀏覽手機上的小紅書內(nèi)容,這種場景在駕駛時尤其有用。
除了底層框架,對于上層業(yè)務(wù),小紅書還有一套自研的組件庫方案,這套組件庫承載了上層業(yè)務(wù)的多種功能,包括圖文筆記、視頻筆記瀏覽,以及一些 Hybrid 容器能力。小紅書本質(zhì)上在跨端開發(fā)中仍然使用了 React Native(RN)和類 Web 技術(shù)。RN 引擎由華為內(nèi)部合作提供,采用了自研的 ohos 方案,用于解決 React Native 的 bundle 和 JS 加載以及渲染問題。此外,還包括產(chǎn)品定制層,這里涵蓋了所有相關(guān)的設(shè)備適配內(nèi)容。

3.4 性能優(yōu)化與實踐
目前,安卓和 iOS 在性能優(yōu)化方面已經(jīng)相當(dāng)成熟,包括如何分析性能熱點問題、有哪些工具以及最佳實踐等。然而,對于鴻蒙來說,它是一個全新的系統(tǒng)。直到 2024 年年中,鴻蒙的穩(wěn)定性和流暢性都還存在一些問題。這里重點講述小紅書在 2024 年與華為一起進行了哪些實踐,以提升應(yīng)用的性能和用戶體驗。
我們定義了一個性能指標(biāo)場景。這個指標(biāo)體系是小紅書與華為共同探討的結(jié)果,因為華為有一個性能工廠,它對每個應(yīng)用的評級都有一個 S 標(biāo)標(biāo)準(zhǔn)。小紅書與華為一起確定了針對小紅書場景需要觀測的具體指標(biāo)。性能優(yōu)化的核心是慢函數(shù)指標(biāo),它主要包含兩部分:過程時長和應(yīng)用體驗的流暢性。
過程時長主要包含以下三點:
- 1)冷啟動時長:這是用戶最關(guān)心的指標(biāo)之一,即從點擊應(yīng)用圖標(biāo)到應(yīng)用完成動畫并展示第一幀的時間。對于多數(shù)應(yīng)用,首頁通常有緩存機制。例如,小紅書會緩存用戶上次刷新的筆記,淘寶會緩存用戶上次瀏覽的商品內(nèi)容;
- 2)場景完成時長:指完成某個特定場景所需的時間;
- 3)應(yīng)用響應(yīng)時長:指用戶操作界面后,界面真正發(fā)生變化的時間,即響應(yīng)時延。
流暢性方面,最基礎(chǔ)的觀測指標(biāo)是平均 FPS(幀率),包括丟幀數(shù)、最大連續(xù)丟幀數(shù)、丟幀卡頓次數(shù)以及卡頓率。卡頓率可以通過量化計算得出:當(dāng)一個場景中出現(xiàn)丟幀時,丟幀的時長與場景總時長的比值即為卡頓率,它是一個小于 1 的百分比數(shù)值。
3.5 OS 能力 & 優(yōu)化實踐
首先,針對 IO 場景,我們進行了相應(yīng)的優(yōu)化。
鴻蒙 OS 的系統(tǒng)能力主要分為以下三個方面:
- 1)并行化能力鴻蒙 OS 提供了兩種并行化能力:Worker 和 TaskPool。Worker 類似于傳統(tǒng)的線程模型,每個 Worker 都有自己的內(nèi)存空間和執(zhí)行單元,支持通過消息(message)進行通信。TaskPool 則類似于協(xié)程或線程池,能夠動態(tài)管理線程數(shù)量,支持標(biāo)記為 @concurrent 的函數(shù)直接在任務(wù)池中調(diào)度和運行。這兩種機制都支持線程間隔離,內(nèi)存不共享;
- 2)多線程通信和數(shù)據(jù)傳輸在多線程通信方面,鴻蒙 OS 支持序列化數(shù)據(jù)傳輸和基于消息(message)的通信機制。此外,還引入了事件發(fā)射器(Emitter)用于系統(tǒng)事件的發(fā)布和訂閱。這種機制允許線程間通過消息傳遞來實現(xiàn)復(fù)雜的交互邏輯;
- 3)同步轉(zhuǎn)異步機制鴻蒙 OS 支持基于 Promise 的異步編程模型,包括 async 和 await 語法,以及 then 和 catch 方法。這種機制能夠有效提升應(yīng)用的響應(yīng)性和用戶體驗。
4、并行化能力
在并行化能力方面,鴻蒙 OS 提供了兩套基礎(chǔ)實現(xiàn)方式。開發(fā)者可以通過 RTS(運行時系統(tǒng))實現(xiàn)并行化,也可以通過底層庫(如 C++ 標(biāo)準(zhǔn)庫中的)實現(xiàn)。不過,如果完全依賴底層庫,可能會導(dǎo)致開發(fā)效率下降。為了滿足業(yè)務(wù)需求,鴻蒙 OS 在年初引入了 Worker 和 TaskPool 能力。Worker 類似于傳統(tǒng)的線程模型,每個 Worker 都有獨立的內(nèi)存空間和執(zhí)行單元,支持通過消息進行通信。消息可以包含可序列化的數(shù)據(jù),也可以通過指針直接遷移數(shù)據(jù)。TaskPool 則類似于線程池,能夠動態(tài)管理線程數(shù)量,支持標(biāo)記為 @concurrent 的函數(shù)直接在任務(wù)池中調(diào)度和運行。與安卓平臺的線程池不同,鴻蒙 OS 的 TaskPool 會根據(jù)硬件條件和任務(wù)負載動態(tài)調(diào)整線程數(shù)量。這種機制避免了安卓平臺中因線程池數(shù)量過多而導(dǎo)致的系統(tǒng)資源消耗問題。
接下來我們對比鴻蒙 OS 的 Worker 并行化能力和安卓端的相關(guān)特性。從多個維度來看,Worker 本質(zhì)上不推薦手動創(chuàng)建,而是通過系統(tǒng)配置 build-provider.json 綁定 ETS 文件來實現(xiàn)創(chuàng)建。這一點與安卓端并無明顯差異,安卓端可以通過 THREAD 等方式啟動線程。
在鴻蒙 OS 5.0 以下版本(如 4.2 版本)中,主要運行的仍然是安卓系統(tǒng)。這種情況下,安卓線程數(shù)量存在上限,這對應(yīng)用開發(fā)者來說是一個挑戰(zhàn)。如果 SDK 集成過多,線程數(shù)可能超標(biāo),進而導(dǎo)致應(yīng)用被系統(tǒng)強制終止,或出現(xiàn)業(yè)務(wù)場景異常崩潰等穩(wěn)定性問題。
數(shù)據(jù)傳輸方面:鴻蒙 OS 為了優(yōu)化 Worker 的性能和負載,對 Worker 的數(shù)量和單個 Worker 的傳輸上限進行了限制。鴻蒙 Worker 的單個傳輸上限類似于安卓中的 Binder 機制,也存在類似的傳輸限制。不過,安卓線程通常沒有嚴(yán)格限制,因為線程本質(zhì)上是一個內(nèi)存拷貝過程,除非開發(fā)者通過指針等方式自定義線程間數(shù)據(jù)傳輸。
在傳輸格式上:鴻蒙 OS 支持通過 Sendable 接口進行數(shù)據(jù)傳輸。Sendable 是一種注解方式定義的數(shù)據(jù)結(jié)構(gòu),具有傳染性,即如果一個類被標(biāo)記為 Sendable,其關(guān)聯(lián)屬性也必須是 Sendable 類型。鴻蒙 OS 支持基礎(chǔ)數(shù)據(jù)類型(如 number、string)和集合類型作為 Sendable 傳輸?shù)膬?nèi)容。對于跨模塊調(diào)用,鴻蒙 OS 不允許 Worker 跨 HAP 或跨 HSP 調(diào)用。相比之下,安卓應(yīng)用通常運行在一個或多個 Dex 文件中,允許跨 Dex 或跨模塊的線程間調(diào)用。
TaskPool 類似于雙端的協(xié)程概念,是一種輕量級線程,僅存儲函數(shù)。不過,TaskPool 與協(xié)程有所不同,它獨立于任務(wù)維度,且任務(wù)執(zhí)行時長有限制(超過 3 分鐘會被系統(tǒng)自動回收)。安卓平臺可以通過 ASM 插樁技術(shù)對線程的創(chuàng)建和執(zhí)行進行監(jiān)控和優(yōu)化,但輕量級線程或協(xié)程的實現(xiàn)通常依賴于線程池或協(xié)程機制。
TaskPool 中的任務(wù)默認支持?jǐn)?shù)據(jù)轉(zhuǎn)移(transfer),不支持拷貝。此外,TaskGroup 不支持 SDK 初始化包的加載。某些同學(xué)習(xí)慣在異步線程中觸發(fā) SDK 的行為,在鴻蒙 OS 上可能會因 TaskPool 生命周期結(jié)束而導(dǎo)致變量被釋放。
關(guān)于并行化數(shù)據(jù)傳輸?shù)?Sendable 概念:Sendable 通過系統(tǒng)提供的 SharedHeap(共享堆)實現(xiàn)傳輸。共享堆與本地堆(local Heap)的區(qū)別在于,共享堆支持 Sendable 化數(shù)據(jù)的傳輸,而本地堆則需要序列化。共享堆的管理和控制耗費了華為專家大量時間和精力,其中還涉及復(fù)雜的異步鎖(async lock)機制。在 RTS 并發(fā)實例期間(包括 Worker、TaskPool 等),數(shù)據(jù)可以通過 Sendable 傳遞,但 Worker 需要使用單獨的 API。TaskPool 則完全支持 Sendable 的直接傳輸。這種異步鎖機制允許在 TaskPool 或 Worker 中鎖定其他任務(wù)中的某些函數(shù),實現(xiàn)線程間的同步,類似于安卓中的 synchronized 或其他鎖機制。
5、小紅書典型并行化場景
小紅書在一些典型化場景中已經(jīng)實現(xiàn)了并行化處理。例如,網(wǎng)絡(luò)請求是一個典型的耗時操作,因為請求過程中涉及驗簽和安全能力的處理,這些操作如果在主線程中同步完成,可能會導(dǎo)致應(yīng)用掉幀。當(dāng)用戶滑動時,掉幀現(xiàn)象會非常明顯,這通常是由于大量計算引起的。為了解決這一問題,我們采用了 Worker 化的方式,將這些操作移到 Worker 線程中,從而避免主線程的卡頓。
在進行埋點時,可能會涉及數(shù)據(jù)庫的 IO 操作,這些操作也不建議在主線程中執(zhí)行。通過將這些操作放到 Worker 線程中,可以有效避免對主線程的影響。
針對雙列布局中的圖片和資源預(yù)加載,我們采用華為自研的 RCP 網(wǎng)絡(luò)解決方案(類似于 HTTP),通過 Worker 線程在遠端進行下載,并在完成后將結(jié)果返回到主線程。此外,TaskPool 的應(yīng)用場景也非常廣泛,例如文件上傳、多媒體操作以及啟動任務(wù)的編排等。TaskPool 的優(yōu)勢在于輕量化,避免了線程上下文切換帶來的不必要耗時。
關(guān)于冷啟動和首刷場景的優(yōu)化。這部分主要包括兩個方面:模塊的懶加載和動態(tài)組件的復(fù)用池。懶加載是應(yīng)用開發(fā)中常見的優(yōu)化手段,類似于安卓端的 class order 機制。當(dāng)應(yīng)用不需要某個類時,可以延遲加載該類,直到真正需要使用時才加載。這種方式可以顯著提高冷啟動階段的代碼加載效率,從而大幅降低冷啟動時長。
動態(tài)組件和組件復(fù)用池則是為了解決 UI 組件重復(fù)創(chuàng)建的問題。在應(yīng)用中,可能會有多種相同類型的 UI 組件(例如小紅書中的筆記組件)。為了避免重復(fù)創(chuàng)建帶來的開銷,我們希望在運行時盡量復(fù)用已有的組件,而不是頻繁地創(chuàng)建和銷毀。
6、類前端視角下的模塊懶加載
我們通過特定的分析工具對懶加載進行了深入分析。如圖所示,我們能夠識別出啟動過程中加載的各種模塊,包括 RNOH(React Native on Harmony)、Web engine(網(wǎng)頁引擎)、Red Player(播放器)等組件。這些模塊的加載過程涉及到多個.so 文件,即共享對象文件。

通過自上而下的分析方法,我們可以清晰地看到每個模塊加載的具體耗時。進一步分析這些.so 文件與 RTS(運行時系統(tǒng))的關(guān)聯(lián),以及它們所引入的 Napi 的 TS 文件。我們進行了懶加載潛在對象的分析,發(fā)現(xiàn)許多 RTS 實際上并不需要的類文件已經(jīng)被加載。這是因為開發(fā)者在編寫代碼時,可能并未充分考慮到導(dǎo)入一個類或方法對應(yīng)用啟動延遲的影響。
為了優(yōu)化這一過程,我們的目標(biāo)是減少字節(jié)碼中需要加載的類文件數(shù)量,從而加快應(yīng)用的冷啟動速度。華為提供的編譯器能夠?qū)?RTS 編譯成 Ark bytecode(方舟字節(jié)碼),這是一種高效的字節(jié)碼格式。通過減少需要加載的類文件數(shù)量,我們可以顯著提高應(yīng)用的啟動速度。
華為還提供了一種懶加載的導(dǎo)入方式,只有在真正需要使用某個類時,它才會被加載。這種懶加載機制有助于減少應(yīng)用啟動時的資源消耗。這引發(fā)了一個問題:為什么華為不默認采用全懶加載方式,即只有在使用時才加載類文件呢?我已經(jīng)將這個問題反饋給華為,并且系統(tǒng)側(cè)可能會考慮在未來的版本中默認采用懶加載方式,同時仍然允許用戶手動選擇非懶加載的方式進行類文件的加載。
7、動態(tài)組件
在小紅書的首頁場景中,筆記卡組件在多個場景中被復(fù)用。為了避免重復(fù)創(chuàng)建 UI 導(dǎo)致的性能消耗,我們采用了動態(tài)組件的概念。動態(tài)組件的核心原理是利用占位符來延遲組件的創(chuàng)建,這與 Android 開發(fā)中使用 Stub 模式的概念相似。在這種模式下,可以使用一個代理對象(stub)來代表尚未初始化的組件,從而延遲組件的創(chuàng)建過程。當(dāng)真正需要渲染組件時,再將渲染內(nèi)容填充進去,從而避免每次調(diào)用構(gòu)建函數(shù)(如 build)時的耗時。
占位邏輯通過系統(tǒng)的 API 實現(xiàn),涉及到 NodeContainer 和 NodeController 的綁定關(guān)系。Container 和 Controller 一一映射,由 NodeCore 進行管理。Container 僅管理當(dāng)前展現(xiàn)的內(nèi)存部分,使用完畢后需要將其放回池中進行回收和再利用。以冷啟動首刷為例,在啟動階段可以先獲取磁盤上的筆記內(nèi)容,然后在 BuilderNode 中預(yù)先創(chuàng)建多個 Image 組件。這樣,在等待網(wǎng)絡(luò)或推薦接口響應(yīng)時,Image 組件已經(jīng)創(chuàng)建完畢,從而在首頁刷新時可以立即使用這些組件,這對于提高首刷非常有益。

對于組件復(fù)用池,當(dāng)動態(tài)組件不再使用時,需要將其返回到組件池中。對于自定義組件,通過 NoteContainer 占位方式,由 NodeController 進行管理。在需要創(chuàng)建子組件時,先在 NodePool 中查找,如果找不到,則創(chuàng)建新組件;如果找到,則嘗試復(fù)用。流程圖展示了從 Container 裝載 NodeItem 開始,通過 NodePool 查找,如果找到則進行條件判斷和復(fù)用。
組件的新建和復(fù)用過程中,如果找到對應(yīng)的 NodeItem,則調(diào)用 build 方法并更新自定義組件的狀態(tài),完成復(fù)用。如果有對應(yīng)的 NodeItem,可以直接通過 update 函數(shù)更新內(nèi)部狀態(tài)并刷新 UI。但要注意,update 方法可能會因狀態(tài)變量過于復(fù)雜而導(dǎo)致更新延遲,出現(xiàn)圖像殘影。因此,需要拆分 state,使其足夠小,以確保狀態(tài)變更到通知 UI 的時間縮短,消除殘影。
我們的策略是優(yōu)先在 NodePool(節(jié)點池)中查找可用的 NodeItem(節(jié)點項)。如果 NodePool 中存在可用的 NodeItem,我們就直接使用它,并通過 getNode 方法進行 item 綁定,隨后更新其狀態(tài)以實現(xiàn)復(fù)用。如果 NodePool 中沒有找到對應(yīng)的 NodeItem,那么我們將通過 makeNode 方法調(diào)用 build 函數(shù)來創(chuàng)建新的節(jié)點項。
完成組件的復(fù)用后,我們需要將這些組件返回到緩存池中,以便在未來可以再次使用。這個過程涉及到 NodeContainer(節(jié)點容器)和 NodeController(節(jié)點控制器)的銷毀,并將 NodeItem 重新放回 NodePool 中。為了更有效地管理緩存,業(yè)務(wù)層可以利用 LRU(最近最少使用)算法,或者鴻蒙系統(tǒng)提供的 LRUCache 和 LiUHashMap 等數(shù)據(jù)結(jié)構(gòu),來自定義緩存的大小,從而優(yōu)化組件的復(fù)用和緩存策略。
8、滑動類場景
在小紅書應(yīng)用中,滑動類場景非常普遍,包括推薦頁的子頻道、個人頁中的收藏點贊以及用戶自己發(fā)布的筆記,還有搜索結(jié)果頁中的搜索結(jié)果和用戶商品等,這些都是雙列滑動場景。這些雙列滑動場景占據(jù)了小紅書用戶體驗的 90% 到 95%,因此,滑動體驗的流暢性對于用戶的整體體驗至關(guān)重要。
為了提升滑動場景的流暢性,小紅書采用了 RCP 框架來優(yōu)化網(wǎng)絡(luò)資源的獲取。RCP 是華為提供的一個系統(tǒng)組件能力,主要解決網(wǎng)絡(luò)資源獲取效率問題。通過 RCP,開發(fā)者可以在需要時發(fā)起網(wǎng)絡(luò)請求,并自定義資源的寫入地址,如文件或 ArrayBuffer。RCP 負責(zé)高效地將資源寫入指定位置,而在不需要時,可以取消 RCP 請求,從而優(yōu)化資源管理。

RCP 的核心能力在于能夠取消請求,并對弱網(wǎng)場景進行了優(yōu)化,其建聯(lián)過程優(yōu)于 HTTP 1.1 或 2.0。基于 RCP,小紅書還應(yīng)用了華為俄研所提供的 Prefetch 方案。Prefetch 方案在瀑布流組件的可見區(qū)變更時,通過 worker 線程(如 prefetched worker)啟動資源獲取,當(dāng)不可見時關(guān)閉,從而優(yōu)化快速滑動場景,減少不必要的帶寬消耗。
在快速滑動過程中,有些 item 可能短暫消失,對于雙端場景,網(wǎng)絡(luò)請求可能已經(jīng)發(fā)出且在途,無法取消,導(dǎo)致帶寬浪費。Prefetch 和 RCP 結(jié)合的方式可以優(yōu)化這種快滑場景,防止真正想要看的內(nèi)容出現(xiàn)白塊。Prefetched worker 線程管理多個 RCP 請求,每個請求都有完整的生命周期。當(dāng)通過 RCP 請求獲取到所需資源時,會通知主線程,主線程根據(jù)地址加載資源到 Image 組件或占位符 RQI 組件中。
在小紅書的開發(fā)過程中,我們遇到了一些性能熱點問題,這些問題大多是通過 Code Linter(代碼檢查工具)檢測出來的。由于開發(fā)節(jié)奏快,開發(fā)者在編寫代碼時可能難以關(guān)注到性能問題,因此需要 CI(持續(xù)集成)檢查工具來輔助檢查。
常見的性能熱點包括:
1)在列表場景中頻繁使用的 LadyForEach 組件,需要指定 key 以實現(xiàn)列表復(fù)用。如果開發(fā)者忘記指定 key,Code Linter 會報錯提示;
2)在 onClick 或 onVisible 等函數(shù)中編寫空 callback(回調(diào)函數(shù))。當(dāng)這些空 callback 積累到一定數(shù)量(如幾百個或上千個)時,可能會嚴(yán)重拖慢應(yīng)用性能。Code Linter 可以掃描出這類問題;
3)未使用 TaskPool 處理網(wǎng)絡(luò)資源。例如,Image Bitmap 直接傳遞 URL 進行同步加載,當(dāng)網(wǎng)絡(luò)阻塞時會導(dǎo)致 UI 線程卡頓;
4)復(fù)雜的 ETS 組件在列表場景下未實現(xiàn)重用。未設(shè)置重用的 ETS 組件在列表滾動時需要重新構(gòu)建,非常耗時。組件嵌套層級過深也會導(dǎo)致性能問題。在安卓端,布局檢查器建議容器嵌套不超過四層;
5)使用 JSON.stringify 進行對象序列化。JSON.stringify 有一定耗時,尤其在處理 100KB 左右的數(shù)據(jù)時,可能需要 10 毫秒左右。Code Linter 會提示這部分性能問題,但是否需要轉(zhuǎn)異步線程需要開發(fā)者自行判斷;
6)調(diào)用 Image 的 syncLoad(同步加載)。在某些場景下,如轉(zhuǎn)場動畫,需要同步加載 image 以保證連貫性。但如果 image 是非磁盤資源(如網(wǎng)絡(luò)資源),會導(dǎo)致卡幀。Code Linter 可以掃描出這類問題;
7)關(guān)于編譯器的優(yōu)化。ETS 組件應(yīng)避免嵌套過深。如果嵌套過深,可以將每層函數(shù)通過系統(tǒng)的 builder param 或 builder 函數(shù)轉(zhuǎn)換。使用 @builder 注解標(biāo)識的函數(shù)會在編譯期間與 ETS 代碼整合,從而提高編譯器優(yōu)化效果。
Code Linter 支持全量掃描和基于 Git DIFF 的增量掃描,但目前華為的 Code Linter 還不能與 Git Prehook 關(guān)聯(lián),導(dǎo)致無法在流水線上自動檢查。雖然 CI 檢查階段已有 Code Linter,但本地代碼提交階段仍需手動運行腳本,無法實現(xiàn)自動檢查。我們正在催促華為解決這一問題。
9、UI 重載場景分幀方案
在處理 UI 重載場景時,我們采用了一種稱為分幀方案的方法。分幀這個術(shù)語的含義是,當(dāng)應(yīng)用在一幀內(nèi)無法完成所有繪制工作,或者在多幀內(nèi)都無法完成時,會導(dǎo)致屏幕卡頓現(xiàn)象。盡管用戶可以看到畫面,但卻無法進行滑動或操作。在這種情況下,分幀方案就顯得尤為合適。雖然分幀方案可能看起來不是最優(yōu)雅的解決辦法,但它確實能夠有效地解決性能問題,使應(yīng)用性能達到預(yù)期標(biāo)準(zhǔn)。分幀方案雖然看似是一種應(yīng)急措施,但它能夠幫助應(yīng)用性能達標(biāo)。
分幀方案的流程大致如下:假設(shè)我們有數(shù)據(jù) a、b、c 需要渲染,未采用分幀方案前,數(shù)據(jù) a、b、c 會同時到達并觸發(fā)狀態(tài)變更,進而驅(qū)動整個 UI 進行刷新。這會導(dǎo)致在一幀內(nèi)需要繪制大量 UI 組件,從而影響應(yīng)用性能。為了解決這個問題,我們采用分幀方案,將數(shù)據(jù) a、b、c 拆分開,分別在不同的幀中進行渲染。例如,數(shù)據(jù) a 在第一幀中渲染完成后,通過調(diào)用宏觀指令讓其進入下一階段,然后在下一幀中更新數(shù)據(jù) b,依此類推。
在小紅書的圖文筆記場景中,分幀方案得到了應(yīng)用。當(dāng)用戶在首頁的雙列場景中點擊一篇筆記進入筆記詳情頁時,這個過程涉及到許多組件的加載。我們可以將這些組件拆分成不同的幀,例如幀 a、幀 b 和幀 c。對于用戶而言,他們通常希望在第一時間看到整個大屏的畫面,因此我們會優(yōu)先在幀 a 中展示大圖。而在幀 b 和幀 c 中,我們再處理頂部導(dǎo)航欄或底部交互區(qū)等內(nèi)容。通過這種分幀策略,我們能夠確保用戶在第一時間看到最關(guān)鍵的內(nèi)容,同時避免了因為一次性加載過多組件而導(dǎo)致的性能問題。
10、鴻蒙NEXT調(diào)優(yōu)工具
傳統(tǒng)的主觀工具對于鴻蒙 OS 的性能分析仍然適用。例如,抖音和小紅書都通過競品分析來進行主觀測評。這種能力主要是通過錄屏來展示整個流程的耗時和時長,特別適合評估冷啟動完成時延和轉(zhuǎn)場過程的性能。通過錄屏,我們可以逐幀查看用戶從點擊開始到結(jié)束的幀數(shù)和真實時長,以此來衡量整個過程的持續(xù)時間。
10.1 鴻蒙性能分析工具:IDE Profiler
除了主觀工具,我們還可以使用 IDE 提供的性能分析工具,如 Profiler,來分析慢函數(shù)。由于 ArkTS 編程語言框架主要通過 RTS 和 NAPI(原生應(yīng)用接口)進行關(guān)聯(lián),因此需要能夠查看 ArkTS 和 NAPI 的整個堆棧層級。這與安卓有所不同,因為當(dāng) Java 通過 Java Native API 與原生代碼交互時,堆棧并不那么容易查看。
在小紅書的性能分析中,我們展示了一個整體線程分析的例子。在左側(cè),可以看到小紅書的主線程(如 com 點開頭的線程)、Daemon 線程、Worker 線程以及 FFRT 線程。FFRT 是一種運行函數(shù)流的線程,可以執(zhí)行 TaskPool 上的函數(shù)。在下圖右側(cè),我們可以看到在 RTS 環(huán)境下的分析結(jié)果,其中頂部顯示了 NAPI 調(diào)用,底部則是一些 C++ 函數(shù)。整個調(diào)用棧和它們的執(zhí)行時長是通過一種自上而下的視圖來展示的。利用這種視圖,我們可以精確地識別出哪些慢函數(shù)是造成界面卡頓的原因。

10.2 性能場景測試工具:DevEco Testing
DevEco Testing 是一個性能測試工具,它的功能非常全面,性能測試只是其中的一部分。除了性能測試,它還支持多種測試場景,包括 debug testing。在 debug testing 場景中,用戶可以自定義業(yè)務(wù)場景,監(jiān)測 CPU 的耗時和負載、GPU 的耗時和負載、設(shè)備發(fā)熱情況以及功耗等問題。

使用 DevEco Testing 進行性能測試的過程如下:首先定義測試場景,然后捕獲主幀數(shù)據(jù)。一旦開始捕獲,就可以觀測到 FPS(幀率)、GPU 負載以及整體功耗等數(shù)據(jù)。完成性能數(shù)據(jù)捕獲后,工具會生成一份報告,為用戶提供了一個完整的場景分析。不過,目前場景定義還缺乏腳本化能力,需要人工操作輔助。未來,我們期望能夠?qū)崿F(xiàn)場景定義的腳本化配置,類似于自動化測試。這樣,就可以通過自動化工具,實現(xiàn)更高效的測試流程。
11、小結(jié)與展望
在對性能場景進行優(yōu)化后,我們可以看到顯著的收益。在實驗室環(huán)境下的測試顯示,冷啟動時間可以降低 50%,響應(yīng)時延可以低于 100 毫秒,完成時延則保持與雙端持平或更優(yōu)。在流暢性方面,在多場景和重載場景下均實現(xiàn)了 0 丟幀的成果。需要注意的是,這里的測試是在非重載模式下進行的,即沒有同時運行多個資源密集型應(yīng)用,如《王者榮耀》或《和平精英》等。在這種條件下,我們的核心場景,如冷啟動、搜索和個人頁等,都能夠與雙端完全對齊。

展望未來,有幾個方向:
1)首先:我們希望能夠在全場景下實現(xiàn)組件復(fù)用,以最大程度地實現(xiàn) UI 復(fù)用。這樣可以在多個業(yè)務(wù)之間的轉(zhuǎn)場或 UI 創(chuàng)建過程中,將不必要的 UI 創(chuàng)建和消耗降到最低。
2)其次:我們正在考慮代碼延遲加載的 lazy 機制。華為內(nèi)部可能將其作為通用的解決方案,但在實施過程中我們發(fā)現(xiàn)了許多問題,例如全 lazy 加載可能會影響第三方 SDK,如支付寶等,因為它們可能進行了額外的二進制優(yōu)化,導(dǎo)致加載失敗或無法響應(yīng)。因此,我們期望通過代碼延遲加載來實現(xiàn)持續(xù)治理,但目前它可能還不適合全場景的 lazy import。
3)最后:我們關(guān)注防劣化問題,即在每個版本發(fā)布時,我們不希望性能指標(biāo)出現(xiàn)劣化。我們希望能夠在開發(fā)階段就定義劣化指標(biāo)和具體數(shù)據(jù),以防止應(yīng)用劣化。這部分可能需要借助 DevEco Testing 和主觀測評的方式來實現(xiàn)。包括我們關(guān)注的指標(biāo),例如冷啟動和流暢性等,未來可能會納入防劣化場景。目前,我們的 CI 環(huán)節(jié)或 RC 環(huán)節(jié),包括流水線的性能管控和代碼 CR 機制,都能夠規(guī)避這類問題。
12、相關(guān)資料
[1] 鴻蒙NEXT官方開發(fā)指南
[2] 一年擼完百萬行代碼,企業(yè)微信的全新鴻蒙NEXT客戶端架構(gòu)演進之路
[3] 鴻蒙NEXT如何保證應(yīng)用安全:詳解鴻蒙NEXT數(shù)字簽名和證書機制
[4] 開源IM聊天程序HarmonyChat:基于鴻蒙NEXT的WebSocket協(xié)議
[5] 微信純血鴻蒙版正式發(fā)布,295天走完微信14年技術(shù)之路!
[6] 即時通訊框架MobileIMSDK的鴻蒙NEXT端詳細介紹
[7] 即時通訊框架MobileIMSDK的鴻蒙NEXT端開發(fā)者手冊
[8] 擁抱國產(chǎn)化:轉(zhuǎn)轉(zhuǎn)APP的鴻蒙NEXT端開發(fā)嘗鮮之旅
[9] 微信Windows端IM消息數(shù)據(jù)庫的優(yōu)化實踐:查詢慢、體積大、文件損壞等
[10] 微信技術(shù)分享:揭秘微信后臺安全特征數(shù)據(jù)倉庫的架構(gòu)設(shè)計
[11] IM跨平臺技術(shù)學(xué)習(xí)(九):全面解密新QQ桌面版的Electron內(nèi)存優(yōu)化實踐
[12] 企業(yè)微信針對百萬級組織架構(gòu)的客戶端性能優(yōu)化實踐
[13] 揭秘企業(yè)微信是如何支持超大規(guī)模IM組織架構(gòu)的——技術(shù)解讀四維關(guān)系鏈
[14] 微信團隊分享:詳解iOS版微信視頻號直播中因幀率異常導(dǎo)致的功耗問題
[15] 微信團隊分享:微信后端海量數(shù)據(jù)查詢從1000ms降到100ms的技術(shù)實踐
[16] 大型IM工程重構(gòu)實踐:企業(yè)微信Android端的重構(gòu)之路
[17] IM技術(shù)干貨:假如你來設(shè)計微信的群聊,你該怎么設(shè)計?
[18] 微信團隊分享:來看看微信十年前的IM消息收發(fā)架構(gòu),你做到了嗎
[19] 總是被低估,從未被超越,揭秘QQ極致絲滑背后的硬核IM技術(shù)優(yōu)化
[20] 首次公開,最新手機QQ客戶端架構(gòu)的技術(shù)演進實踐
[21] 大型IM穩(wěn)定性監(jiān)測實踐:手Q客戶端性能防劣化系統(tǒng)的建設(shè)之路
(本文已同步發(fā)布于:http://www.52im.net/thread-4821-1-1.html)