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