
使用DataCache三方库提升应用冷启动速度 原创
介绍
在鸿蒙生态开发中,DataCache作为三方库之一的高性能组件,针对应用冷启动耗时问题提供了创新解决方案。该组件通过优先加载本地缓存数据,显著缩短应用启动时间,优化用户体验。其内置的通用本地缓存库,支持开发者高效实现缓存逻辑,既提升开发效率,又增强应用响应速度。DataCache采用先进缓存策略,确保数据一致性与快速访问,特别适用于频繁读取相同数据且更新不频繁的场景。本案例将基于DataCache库的数据缓存能力,提升HarmonyOS应用冷启动速度的功能,以优化应用使用体验。
说明
本案例是参考官方示例代码案例学习,从中学到DataCache三方库的使用,使用Refresh组件和List组件实现下拉刷新,上滑加载更多知识点,每次学习官方示例代码或Codelab都能学习到新的知识点,大家也可以尝试打开两个DevEco,一个是打开参考案例,一个是自己创建的项目,最好代码块里的目录和文件,代码都是亲自手工添加的,不能为了方便复制、粘贴,这样不利用理解参考案例的思路。
效果预览
工程目录
├──entry/src/main/ets/
│ ├──commons
│ │ ├──CommonConstants.ets // 公共常量
│ │ └──CommonTypes.ets // 公共类型型
│ ├──utils
│ │ └──Refresh.ets // 刷新工具类
│ ├──entryability
│ │ └──EntryAbility.ets // 程序入口类
│ ├──model
│ │ └──NewsModel.ets // 模型文件
│ ├──pages
│ │ └──Index.ets // 首页
│ └──view
│ ├──ListPage.ets // News列表页
│ ├──NewsItem.ets // 列表item项
│ └──TabBar.ets // TabBar
└──entry/src/main/resources // 应用资源目录
具体实现
安装DataCache三方库
ohpm install @hadss/datacache
EntryAbility.ets使用三方库
-
定义LocalStorage本地存储变量和网络请求http变量
export default class EntryAbility extends UIAbility { // LocalStorage是ArkTS为构建页面级别状态变量提供存储的内存内的“数据库”。 // 构建本地存储 storage: LocalStorage = new LocalStorage() // 创建一个HTTP请求,里面包括发起请求、中断请求、订阅/取消订阅HTTP Response Header事件。 net = http.createHttp() ...... }
-
定义本地缓存获取和存储方法
/** * 获取本地缓存数据 * @returns */ async getCache(): Promise<ResData[] | undefined> { const dataCache = DataCache.getInstance(this.context) const listData = await dataCache.get<ResData[]>('List') if (listData) { // 存储到LocalStorage this.storage.setOrCreate('List', listData); } return listData; } /** * 设置本地缓存数据 * @param data */ async setCache(data: ResData[]) { const dataCache = DataCache.getInstance(this.context) const arrData: Array<Promise<void>> = [] data.forEach(it => { it.imagesUrl.forEach(img => { arrData.push((async() => { const url = await this.net.request(img.url) let base64 = new util.Base64Helper() let arr = new Uint8Array(url.result as ArrayBuffer) let base64Str = IMAGE_BASE64_PREFIX + base64.encodeToStringSync(arr) img.url = base64Str })()) }) }) await Promise.all(arrData) // 有效期为2天 dataCache.put('List', data, 2*24*60*60) }
-
在onCreate回调函数添加数据缓存
// 冷启动时,从本地缓存获取数据,没缓存数据,请求网络数据 this.getCache().then((cache) => { if (!cache) { this.net.request(`${getUrl(this.context)}pageNum=1&pageSize=${PAGE_SIZE}`).then((data) => { const resData: ResData[] = JSON.parse(data.result as string).data this.storage.setOrCreate('List', resData) this.setCache(resData) }).catch(() => { hilog.info(DOMAIN, 'testTag', '%{public}s', '网络请求失败'); this.storage.setOrCreate('Fail', true) }) } })
-
加载页面时,记得、记得、记得把上面定义的LocalStorage变量作为参数传入,不然在页面获取不到LocalStorage存储的数据。
onWindowStageCreate(windowStage: window.WindowStage): void { // 记得在loadContent页面时,把localStorage变量传入 windowStage.loadContent('pages/Index', this.storage, (err) => { if (err.code) { hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); return; } hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.'); }); }
Index.ets页面接收LocalStorage
-
获取当前UIAbility实例内共享的LocalStorage实例
let storage = LocalStorage.getShared() @Entry(storage) @Component struct Index { build() { Column() { TabBar() } .height(FULL_HEIGHT) .width(FULL_WIDTH) .backgroundColor('#F1F3F5') } }
-
ListPage.ets组件监听LocalStorage存储数据
export default struct ListPage { @State isRefresh: boolean = false; @State newsModel: NewsModel = new NewsModel(); // Fail Key 在EntryAbility设置 @LocalStorageLink('Fail') isFail: boolean = false; // List Key 在EntryAbility设置,并且监听数据变化回调getInitData函数 @LocalStorageLink('List') @Watch('getInitData') resData: ResData[] = []; context = getContext(); /** * 初始化数据 */ getInitData() { this.newsModel.currentPage = 1 if (this.resData.length === this.newsModel.pageSize) { this.newsModel.currentPage++; this.newsModel.hasMore = true } else { this.newsModel.hasMore = false } this.newsModel.newsData = this.resData if (this.resData.length !== 0) { this.newsModel.pageState = Const.PageState.SUCCESS } } aboutToAppear(): void { this.getInitData() } ...... }
-
代码分析是基于ListPage.ets组件
build() { Column() { if (!this.isFail && this.newsModel.pageState === Const.PageState.SUCCESS) { // 请求网络成功,状态为成功时,显示新闻列表布局 this.newsLayout() } else if (!this.isFail && this.newsModel.pageState === Const.PageState.LOADING) { // 请求网络成功,状态为正在加载中时,显示加载布局 this.loadingLayout() } else { // 请求网络失败,显示失败布局 this.failLayout() } } .justifyContent(FlexAlign.Center) }
-
失败布局
@Builder failLayout() { Image($r('app.media.none')) .height(120) .width(120) Text('网络加载失败') .opacity(0.6) .fontSize(16) .fontColor('#182431') .margin({ top: 12 }) }
-
刷新布局
@Builder refreshLayout(refresh: Refresh) { Row() { Image(refresh.imageSrc) .width(Const.REFRESH_IMAGE_WIDTH) .height(Const.REFRESH_IMAGE_HEIGHT) Text(refresh.textValue) .margin({ left: Const.REFRESH_TEXT_MARGIN_LEFT, bottom: Const.REFRESH_TEXT_MARGIN_BOTTOM }) .fontSize(Const.REFRESH_TEXT_FONT_SIZE) .textAlign(TextAlign.Center) } .clip(true) .width(Const.FULL_WIDTH) .justifyContent(FlexAlign.Center) .height(refresh.heightValue) }
-
新闻列表布局
@Builder loadingLayout() { // 刷新布局 this.refreshLayout({ isVisible: true, imageSrc: $r('app.media.ic_pull_up_load'), textValue: '加载中...', heightValue: this.newsModel.pullDownRefreshHeight, }) }
-
新闻列表布局
@Builder refreshBuilder() { // 刷新布局 this.refreshLayout({ isVisible: true, imageSrc: this.newsModel.pullDownRefreshImage, textValue: this.newsModel.pullDownRefreshText, heightValue: this.newsModel.pullDownRefreshHeight, }) }
@Builder newsLayout() { // 新闻列表布局使用刷新组件 Refresh({ refreshing: $$this.isRefresh, builder: this.refreshBuilder }) { List() { ForEach(this.newsModel.newsData, (item: ResData) => { ListItem() { NewsItem({ itemData: item }) } .width(Const.FULL_WIDTH) .backgroundColor(Color.White) .margin({ bottom: Const.LIST_ITEM_MARGIN_TOP }) .borderRadius(Const.LIST_ITEM_BORDER_RADIUS) .align(Alignment.Top) }, (item: ResData, index?: number) => `${item.id}${index}`) ListItem() { if (this.newsModel.hasMore) { this.loadMore({ isVisible: true, imageSrc: this.newsModel.pullUpLoadImage, textValue: this.newsModel.pullUpLoadText, heightValue: this.newsModel.pullUpLoadHeight, }) } else { this.noMore() } } } .width(Const.FULL_WIDTH) .height(Const.FULL_HEIGHT) .padding({ left: Const.LIST_MARGIN_LEFT, right: Const.LIST_MARGIN_RIGHT }) .backgroundColor($r('app.color.listColor')) .edgeEffect(EdgeEffect.None) .onScrollIndex((start: number, end: number) => { // 监听当前列表的第一个索引。 this.newsModel.startIndex = start; this.newsModel.endIndex = end; // 加载更新数据 loadMore(this.newsModel, getUrl(this.context)); }) } .width(Const.FULL_WIDTH) .onStateChange((state: RefreshStatus) => { // 刷新状态 refreshState(this.newsModel, state); }) .onRefreshing(() => { // 刷新请求 refreshing(this.newsModel, getUrl(this.context)).finally(() => { this.isRefresh = false; }); }) .refreshOffset(Const.CUSTOM_LAYOUT_HEIGHT) .pullToRefresh(true) }
-
加载更多数据
@Builder loadMore(refresh: Refresh) { Column() { if (refresh.isVisible) { this.refreshLayout(refresh) } } }
-
没有更新数据
@Builder noMore() { Row() { Text($r('app.string.prompt_message')) .margin({ left: Const.NO_MORE_NORMAL_PADDING }) .fontSize(Const.NO_MORE_TITLE_FONT) .textAlign(TextAlign.Center) } .width(Const.FULL_WIDTH) .margin({ bottom: Const.TAB_HEIGHT }) .justifyContent(FlexAlign.Center) .height(Const.CUSTOM_LAYOUT_HEIGHT) }
点击Clear按钮,清除本地缓存数据,下次冷启动会通过网络获取数据。
DataCache.getInstance(getContext(this)).clear().then(() => {
promptAction.showToast({
message: $r('app.string.clear_success'),
duration: Const.ANIMATION_DURATION
});
});
总结
DataCache库是一个高性能的本地缓存解决方案,能够显著提升应用冷启动速度和数据加载效率。通过简单的API接口,开发者可以方便地集成缓存功能,实现数据的快速存储和获取。DataCache还支持缓存失效控制和缓存淘汰机制,帮助开发者有效管理缓存数据,避免缓存污染和内存泄漏。在实际项目中,DataCache库适用于需要频繁读写数据、对性能要求较高的场景,如首页数据缓存、图片预加载等,能够显著提升用户体验和应用性能。
相关权限
- ohos.permission.INTERNET
- ohos.permission.GET_NETWORK_INFO
