OpenHarmony列表場景性能提升方法
OpenHarmony列表場景性能提升方法
- 摘要:列表場景在應用程序中很常見,列表性能非常影響用戶體驗。本文會介紹開發OpenHarmony列表頁面時需要考慮的性能提升方法。
- 關鍵字:OpenHarmony HarmonyOS 鴻蒙 懶加載 列表滑動性能 LazyForEach cachedCount IDataSource
概述
列表場景在應用程序中很常見,比如新聞列表,通訊軟件消息列表,聯系人列表,排行榜,各種賬單等。列表性能非常影響用戶體驗,優化列表性能可以提升用戶交互體驗。在開發OpenHarmony應用時,常用優化列表性能的方法包含:
- 懶加載
懶加載在需要數據時才加載數據。如果一次性加載所有的列表數據,一方面會導致頁面啟動時間過長,影響用戶體驗,另一方面也會增加
服務器的壓力和流量,加重系統負擔。數據懶加載可以從數據源中按需迭代加載數據并創建相應組件。 - 緩存列表項
緩存已經渲染過的列表項可以減少重復渲染的開銷。另外,通過把屏幕外列表項預先加載緩存起來,這樣也可以提升列表響應速度。 - 優化頁面布局
優化渲染布局可以減少不必要的渲染繪制操作,提升列表的流暢度。在優化頁面布局時,需要減少視圖嵌套層次,移除不必要的組件,保持頁面精簡。 - 優化圖片加載
在列表中包含圖片時,優化圖片加載可以提高列表的加載速度和流暢度。如果是小圖片可以同步下載,避免列表快速滑動時產生圖片白塊。 - 組件復用
有些場景下的自定義組件具有相同的組件布局結構,僅有狀態變量等承載數據的差異。把這樣的組件緩存起來,需要使用到該組件時直接復用,減少重復創建和渲染的時間,從而提高應用頁面的加載速度和響應速度。
本文會以一個范例應用來介紹開發OpenHarmony列表頁面時,如何使用上述方法來提升列表性能。
環境準備
本文會基于Sample聊天實例應用進行講解,該示例應用是一個仿聊天類應用,使用了靜態布局搭建了不同的頁面。為了優化內存與性能體驗,在部分列表場景使用了懶加載。除了懶加載,本文還會嘗試其他性能優化方法。
性能實踐
懶加載
開發者在使用長列表時,如果直接采用循環渲染方式,會一次性加載所有的列表元素,會導致頁面啟動時間過長,影響用戶體驗。建議開發者使用數據懶加載,從數據源中按需迭代加載數據并創建相應組件。
在使用數據懶加載之前,需要實現懶加載數據源接口IDataSource,該接口定義在OpenHarmony SDK的接口聲明文件ets\component\lazy_for_each.d.ts里。還需要為數據源注冊數據變更監聽器DataChangeListener,在數據變更時調用相應的回調函數。Sample聊天實例應用中,并沒有使用到數據變更監視器。數據源類BasicDataSource.ets定義如下,該類是一個abstract抽象類,每個列表的數據源具體實現類可以繼承實現該抽象類。
export abstract class BasicDataSource implements IDataSource {
private listeners: DataChangeListener[] = []
public abstract totalCount()
public getData(index: number): any {
return undefined
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener)
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener)
if (pos >= 0) {
this.listeners.splice(pos, 1)
}
}
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded()
})
}
......
}
以聊天列表為例,數據源具體類ChatListData實現如下,其中ChatModel類對應聊天列表中列表項,包含用戶名、最后一條消息內容,時間戳等信息。數據源類ChatListData中,getData()方法為數據源接口IDataSource中定義的,用于給LazyForEach提供數據;addData()和pushData()為數據源類中定義的方法,可用于給數據源增加數據。Sample聊天實例應用中,使用的模擬數據,并沒有從網絡上獲取,這些增加數據的接口實際上未使用,僅用于后續擴展。
class ChatListData extends BasicDataSource {
private chatList: Array<ChatModel> = []
public totalCount(): number {
return this.chatList.length
}
public getData(index: number): any {
return this.chatList[index]
}
public addData(index: number, data: ChatModel): void {
this.chatList.splice(index, 0, data)
this.notifyDataAdd(index)
}
public pushData(data: ChatModel): void {
this.chatList.push(data)
this.notifyDataAdd(this.chatList.length - 1)
}
}
最后看下列表頁面的代碼,詳細代碼見文件ChatListPage.ets。
可以看出,在List組件容器中,使用LazyForEach循環生成ListItem列表項,按屏幕展示需要逐次加載所需的數據,實現了懶加載。如果使用ForEach循環會一次性加載所有的數據。
build() {
Column() {
List() {
......
LazyForEach(this.chatListData, (msg: ChatModel) => {
ListItem() {
ChatView({ chatItem: msg, wantParams: this.wantParams, wantFileParams: this.wantFileParams })
}
}, (msg: ChatModel) => msg.user.userId)
}
.backgroundColor(Color.White)
.listDirection(Axis.Vertical)
......
}
}
緩存列表項
在OpenHarmony SDK文件list.d.ts中,ListAttribute列表屬性類中定義了一個cachedCount屬性。
該屬性cachedCount用于設置長列表延遲懶加載時列表項ListItem的最少緩存數量,表示屏幕外List/Grid預加載項的個數。
應用通過增大List控件的cachedCount參數,調整UI界面的加載范圍。如果需要請求網絡圖片,可以在列表項滑動到屏幕顯示之前,提前下載好內容,從而減少滑動白塊。
在Sample聊天實例應用中,并未使能該屬性,可以嘗試使能該屬性。緩存列表項數量,建議設置為當前列表頁面屏幕可以展示列表項的2倍,具體設置根據列表頁面實際情況進行酌情設置。
如下是使用cachedCount參數的例子,我們設置為緩存20條列表項。當設置cachedCount,可以通過在數據源實現類getData()方法中,添加日志打印來驗證。當列表界面滑動時,除了獲取屏幕上展示的數據,還會額外獲取20條列表項數據緩存起來。
build() {
Column() {
List() {
......
LazyForEach(this.chatListData, (msg: ChatModel) => {
ListItem() {
ChatView({ chatItem: msg, wantParams: this.wantParams, wantFileParams: this.wantFileParams })
}
}, (msg: ChatModel) => msg.user.userId)
}
.backgroundColor(Color.White)
.listDirection(Axis.Vertical)
......
.cachedCount(20)
}
}
優化渲染布局
在OpenHarmony應用開發時,使用ArkUI Inspector分析列表的界面布局。減少嵌套層次,移除不必要的組件來提升頁面響應性能。
可以參考文章《OpenHarmony使用ArkUI Inspector分析布局》了解更多。
優化圖片加載
分析下Image組件的圖片加載流程。syncLoad屬性默認為false,創建圖片時創建一個異步任務,并使用互斥鎖。異步加載圖片可以避免阻塞主線程,影響UI交互,適合圖片加載較長時間時使用。異步任務和使用互斥鎖也是有開銷的,也可能會影響內存和性能,需要根據實際業務情況進行設置。
在列表場景下,快速滑動時,圖片刷新會出現閃爍, 這時可以設置syncLoad屬性為true,使圖片同步加載,從而避免出現閃爍,可以解決快速滑動時產生的圖片白塊。
build() {
......
Row() {
Row() {
Image(this.chatItem.user.userImage)
......
.syncLoad(true)
}
......
}
......
}
組件復用
在OpenHarmony應用開發時,自定義組件被@Reusable裝飾器修飾時表示該自定義組件可以復用。在父自定義組件下創建的可復用組件從組件樹上移除后,會被加入父自定義組件的可復用節點緩存里。在父自定義組件再次創建可復用組件時,會通過更新可復用組件的方式,從緩存快速創建可復用組件。
使用裝飾器@Reusable標記一個組件屬于可復用組件后,還需要實現組件復用聲明周期回調函數aboutToReuse,其參數為可復用組件的狀態變量。調用可復用自定義組件時,父組件會給子組件傳遞構造數據。示例代碼如下所示:
@Reusable
@Component
struct ReusableChatView {
@State chatItem: ChatModel = undefined
aboutToReuse(params) {
this.chatItem = params.chatItem
}
build() {
ChatView({ chatItem: this.chatItem })
}
}
注意事項
如果需要驗證列表滑動時,列表項中組件的掛載卸載,是否在屏幕上展示,可以調用組件的onAppear、onDisAppear事件。
總結
本文基于Sample聊天實例應用中的聊天列表場景,分析了列表滑動性能的優化方法,包含懶加載、緩存列表項、小圖片的同步加載,以及頁面布局優化等。