0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

【鸿蒙】桌面卡片开发教程:从底层原理开始讲透call事件的刷新机制 “坚果派-咸鱼”

王程 来源:jf_75796907 作者:jf_75796907 2024-03-04 10:02 次阅读

坚果派由坚果创建,团队拥有 12 个华为 HDE,以及若干其他领域的三十余位万粉博主运营。
团队成员聚集在北京,上海,南京,深圳,广州,宁夏等地,欢迎合作。

首先铺垫两个基础知识:

1.为什么桌面卡片需要使用特殊机制来刷新?

主要有两个原因:第一是 OpenHarmonyOS Api9 的桌面卡片出于降低系统能耗的目的,被限制了只有 5 秒的活动时间。超过 5 秒以后桌面卡片的相关进程会被强制销毁,变成一个静态的页面。只有通过 router 机制、call 机制或者 message 机制拉起相关后台,才能再次进行卡片内容的刷新。

第二个原因是从实际的运行机制来说,桌面卡片实际上并不是应用主体的一部分,而是归属于 OpenHarmony 系统中的桌面应用所管理的一系列服务,桌面卡片与其对应的应用主体之间相互隔离,只能使用专门的接口来进行数据交互与页面管理。

每张卡片都有一个独立的 LocalStorage 可以用来存储页面级变量,但同一个 LocalStorage 的数据只能在 UIAbility 内部共享,对外隔离,UIAbility 无法直接访问。

桌面卡片刷新机制的本质就是通过专门的接口,改变特定桌面卡片的 LocalStorage 参数。以实现桌面卡片的 UI 更新。

2.router 机制、call 机制与 message 机制有什么不同?

这三个机制都可以用来刷新桌面卡片的,三种机制的数据都以 JSON 的格式进行配置,并使用**formBindingData.createFormBindingData()**函数构建数据对象。

主要区别在于:

router 机制会直接打开应用界面,效果有点像点击桌面图标。也可以带参数打开应用,直接进入应用内部的某个特定位置,或者触发某项功能。

call 机制是不打开应用界面,仅在后台拉起应用主体的 UIAbility,来执行 UIAbility 内部的相关代码。call 机制不受 5 秒时长的限制,可以先实现复杂且费时的数据加载,再提供给桌面卡片进行刷新。

message 机制则不涉及到应用的 UIAbility,只是拉起桌面卡片自己的 FormAbility,也可以刷新卡片,但仍然受 5 秒时长的限制,更适合轻量化的的实现卡片内容的刷新。

接下来进入正式讲解:

本案例使用 call 机制,通过拉起应用主体的方式来刷新卡片内容。

使用 call 机制刷新卡片的全流程主要分为 3 个阶段:

1.通过卡片的 postCardAction 接口触发 call 事件 →

2.call 事件拉起应用主体的后台,进行数据准备,通过 updateForm 接口执行刷新事件 →

3.卡片 page 页面接收到数据,更新卡片界面。

由于卡片与应用主体是独立运作的,并且一个应用可能会有多个应用卡片,应用其实并不知到是哪个卡片触发了 call 事件,所以我们需要把卡片 id 作为参数一起写入接口,让卡片的管理方能找到我们要刷新的卡片。

补全以后的全流程如下:

1.(卡片创建时)FormAbility 获取卡片的 FormID,将其作为参数交给卡片页面储存 →
2.卡片页面初始化自己的 FromID
3.(点击刷新时)卡片通过 postCardAction 接口触发 call 事件,将卡片的 FormID 与要执行的函数名 method 都作为参数提交,由系统启动对应应用的 UIAbility→
4.UIAbility 启动成功,通过预置的触发器执行 method 对应的函数,完成数据准备后通过 updateForm 接口将数据推送给卡片管理方 →
5.对应 FormID 的卡片检测并同步到数据变动,完成页面变更。

案例代码:

1.卡片创建时 FormAbility 获取 FormID,并交给卡片页面

import formBindingData from '@ohos.app.form.formBindingData'; 
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';

export default class EntryFormAbility extends FormExtensionAbility {
  onAddForm(want) {//创建卡片时产生的want中包含FormID,需要再此处读取出来同步给LocalStorage。
   let formId = want.parameters["ohos.extra.param.key.form_identity"];
   let dataObj1 = {
     "formId": formId
   };
   let obj1 = formBindingData.createFormBindingData(dataObj1);
   return obj1;
 }
};


2.卡片页面初始化自己的 FromID

let storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
  @LocalStorageProp('formId') formId: string = '0';//在Form页面中使用@LocalStorageProp来进行FromID变量的初始化
}


3.卡片通过 postCardAction 接口触发 call 事件,让系统拉起应用后台的 pageAbility

let storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
  @LocalStorageProp ("formId") @Watch('firstFresh') formId:string='0'//此处使用一个watch装饰器,在FormID初始化完成后实现第一次的数据刷新。
  @LocalStorageProp('text')movieName: string = '加载中...';
  @LocalStorageProp('imgName')imgName: string = 'imageName';
  firstFresh(){
    console.log('卡片初始化')
    postCardAction(this, {
      "action": 'call',
      "abilityName": this.ABILITY_NAME,
      "params": {
        "method":"funA",
        "formId":this.formId,
      }
    });
  }

  build() {
    Column() {
      Button('刷新')
        .height('15%')
        .onClick(() => {
          postCardAction(this, {
            "action": 'call',//call事件
            "abilityName": 'EntryAbility',//指向目标应用的Ability
            "params": {
              "method":"funA",//UIAbility中注册的用于刷新卡片的事件
              "formId":this.formId,//卡片自身的FormID
            }
          });
        })
  }
}
​

4.pageAbility 启动,执行对应的函数,完成数据准备后通过 updateForm 接口将数据推送给卡片管理方
(数据来源于对应的 severless 服务器,为方便演示,severless 的安全机制设为了不做身份校验即可访问数据的形式)

注意:callee 监听事件必须要先申请"ohos.permission.KEEP_BACKGROUND_RUNNING"权限才能正常运行!!!

import UIAbility from '@ohos.app.ability.UIAbility';
import formBindingData from '@ohos.app.form.formBindingData';
import formProvider from '@ohos.app.form.formProvider';
import hilog from '@ohos.hilog';
import Window from '@ohos.window';
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import {AGCCloudDB} from'../services/AGCCloudDB'
import {AGCStorageReference} from'../services/Storage'
import request from '@ohos.request';
import fs from '@ohos.file.fs';

export default class EntryAbility extends UIAbility {
  onCreate(want, launchParam) {//如果触发call事件时UIAbility未启动,会先执行onCreate再执行监听事件。如果触发Call事件时UIAbility已经在运行中则直接执行监听事件。
     this.callee.on('funA',(str)=>{//监听器,检测到对应参数就执行回调函数
         let params = JSON.parse(str.readString())
         if (params.formId != undefined) {
           let formId = params.formId
           const agcCloudDB = AGCCloudDB.instance(this.context)
           agcCloudDB.init(this.context).then((res) => {
             let CloudDB = new AGCStorageReference(this.context)
             agcCloudDB.getMovie().then((allRecords) => {//从数据库获取数据组
               let radomNumber = Math.floor(Math.random() * allRecords.length)               CloudDB.getDownloadUrl(allRecords[radomNumber].movieName).then((url) => {//从数据组中随机抽取一项,获取对应的图片下载链接
                 let tempDir = this.context.getApplicationContext().tempDir;
                 let tmpFile = tempDir + '/file' + Date.now();
                 request.downloadFile(this.context, {//将图片下载到本地,并得到本地的图片地址
                   url: url, filePath: tmpFile
                 }).then((task) => {
                   task.on('complete', function callback() {
                     console.info('ArkTSCard download complete:' + tmpFile);
                     let file;
                     try {
                       file = fs.openSync(tmpFile);
                     } catch (e) {
                       console.error(`openSync failed: ${JSON.stringify(e)}`);
                     }
                     console.log(JSON.stringify(allRecords))
                     let formData = {//进行数据打包
                       "text": allRecords[radomNumber].movieName,
                       "imgName": allRecords[radomNumber].movieName,
                       'formImages': {},
                      }                     formData.formImages[allRecords[radomNumber].movieName]=file.fd//由于Image的刷新机制必须接受不同的值才能识别到image变化,因此movieName只能使用变量的形式写入。
                     let formInfo = formBindingData.createFormBindingData(formData)//创建FormBindingData对象
                     formProvider.updateForm(formId, formInfo)
                   })//执行updateForm事件,刷新指定卡片。
                   task.on('fail', function callBack(err) {//数据准备失败时显示的内容
                     console.info('ArkTSCard download task failed. Cause:' + err);
                     let formInfo = formBindingData.createFormBindingData({
                       'text': '刷新失败,请重试'
                     })
                     formProvider.updateForm(formId, formInfo)
                   })
                 })
                 return null
               })
             })
           })
         }
     })
  }
}


5.对应 FormID 的卡片收到新的数据,完成页面变更。

let storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
  @LocalStorageProp ("formId") @Watch('firstFresh') formId:string='0'
  @LocalStorageProp('text')movieName: string = '加载中...';
  @LocalStorageProp('imgName')imgName: string = 'imageName';
//LocalStorageProp检测到text与imgName变量发生改变,单向同步最新的数据,并触发卡片页面的文本与图片刷新。

  build() {
    Column() {
      Text(this.text)
        .fontSize('12vp')
        .textAlign(TextAlign.Center)
        .width('100%')
        .height('15%')
      Row() {
          Image('memory://' + this.imgName)//imgName变化以后image组件会自动寻找对应的图片进行加载
            .width('50%')
            .height('50%')
            .margin('5%')
      }.alignItems(VerticalAlign.Center)
      .justifyContent(FlexAlign.Center)
      Button('刷新')
        .height('15%')
        .onClick(() => {
          postCardAction(this, {
            "action": 'call',
            "abilityName": 'EntryAbility',
            "params": {
              "method":"funA",//UIAbility中注册的用于刷新卡片的事件
              "formId":this.formId,
            }
          });
        })
    }
    .width('100%').height('100%')
    .alignItems(HorizontalAlign.Center)
    .padding('5%')
  }
}
​

postCardAction 接口 call 事件的写法如下:

wKgZomXkdaiADVf1AAB2zo6i9kg580.pngwKgaomXkdbGAL1J7AAJzQLigpNo847.png

updateForm 接口的写法如下:

wKgZomXkdbmAHoToAABwqe3Hflk973.png


为了能让大家更好的学习鸿蒙 (OpenHarmony) 开发技术,这边特意整理了《鸿蒙 (OpenHarmony)开发学习手册》,希望对大家有所帮助:

《鸿蒙(Harmony OS)开发学习手册》

入门必看:https://docs.qq.com/doc/DUk51cHZJaUpmSlhH
1.应用开发导读(ArKTS)
2.……

wKgaomXW6N2AJp9uAAQXRxEAprs547.png

HarmonyOS概念:https://docs.qq.com/doc/DUk51cHZJaUpmSlhH
1.系统定义
2.技术框架
3.技术特性
4.系统安全

wKgaomXW6OSAFcCRAAV2zd2X_1s891.png

快速入门:https://docs.qq.com/doc/DUk51cHZJaUpmSlhH
1.基本概念
2.构建第一个ArkTS应用
3.…

wKgZomXW6PuAA7wEAAKx6By_2Z8377.png

开发基础知识:https://docs.qq.com/doc/DUk51cHZJaUpmSlhH
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS
9…

wKgZomXW6QaAM4niAAQzrXUUPik914.png

基于ArkTS 开发:https://docs.qq.com/doc/DUk51cHZJaUpmSlhH
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16………

wKgZomXW6RKATahiAAKz-zSMnR4040.png



审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 鸿蒙
    +关注

    关注

    57

    文章

    2339

    浏览量

    42805
  • HarmonyOS
    +关注

    关注

    79

    文章

    1973

    浏览量

    30143
  • OpenHarmony
    +关注

    关注

    25

    文章

    3713

    浏览量

    16254
收藏 人收藏

    评论

    相关推荐

    TC275HSM能支持SecOC中的密钥刷新机制吗?

    1.请教一下,像TC275HSM 能支持SecOC中的密钥刷新机制吗?即SecOC中的密钥生成节点生成新的密钥后,是如何下发给ECU的HSM中?2. 二代HSM TC3xx 的HSM 集成
    发表于 02-21 06:07

    求大神分享一种基于bootloader的嵌入式软件自动更新机制

    本文提出了一种具有较高稳定性和安全性、基于bootloader的嵌入式软件自动更新机制。该更新机制同时保存了3个文件,需要较多的Flash存储空间,但同时降低了维护成本。
    发表于 04-27 06:33

    #HarmonyOS征文#鸿蒙卡片-物联网DTU污水液位计卡片

    ();}}super.onTriggerFormEvent(formId, message); }}这样点击index.hml中的标题,就可以更新数据了7. 服务卡片进阶开发教程7.1 数据定时刷新7.1.1 使用
    发表于 06-26 08:59

    HarmonyOS卡片开发--服务卡片概述

    运行,在需要添加/删除/请求更新卡片时,卡片管理服务会拉起卡片提供方获取卡片信息。 运作机制 卡片
    发表于 09-22 14:10

    HarmonyOS原子化服务开发实战-卡片刷新图片问题记录

    在一篇文章上看到,卡片刷新图片的方式是回到桌面才进行刷新,所以我在页面的onStop()和onTonInactive()方法上调用卡片
    发表于 12-07 15:15

    Android系统固件更新机制设计资料分享

    Android系统固件更新机制设计说明文档V1.1xxx2014-9-14修改历史记录内容编制\日期审核\日期批准\日期 V1.0建立初稿Xxx2014-9-14 V1.1 增加配图,统一英文单词大小写Android启动过程错误修正,红色字体部...
    发表于 12-20 08:08

    求助,请问鸿蒙卡片如何去掉应用的桌面图标?

    ,但是我想有卡片功能,我卡片功能又不能在原来APP的代码基础上开发开发工具不一样,我创建卡片后运行,会有一个应用图标,设置上滑
    发表于 06-14 10:18

    为什么鸿蒙卡片编辑页面无法上滑返回桌面

      测试使用的是开发工具自带的模板,添加了卡片编辑页面。长按卡片进入卡片编辑页面,无法上滑返回桌面
    发表于 06-17 10:26

    ArkTS语言HarmonyOS/OpenHarmony应用开发-message事件刷新卡片内容

    开发过程 在卡片页面中可以通过postCardAction接口触发message事件拉起FormExtensionAbility,然后由FormExtensionAbility刷新卡片
    发表于 06-12 14:42

    HarmonyOS元服务开发实践:桌面卡片字典

    本文转载分享自华为开发者论坛《​HarmonyOS元服务开发实践:桌面卡片字典​》,作者:蛟龙腾飞 一、项目说明 1.DEMO创意为卡片字典
    发表于 08-24 16:55

    ADO_NET数据集更新机制及并发控制策略

    ADO_NET数据集更新机制及并发控制策略:本文分析了8I5J (?> 中的更新机制,论述了三种不同的更新逻辑的产生方式及各自特点,提出了并发控制的一些解决方法,及更新逻辑中其他一
    发表于 01-01 18:48 12次下载

    嵌入式系统自更新机制的设计与应用

    嵌入式系统自更新机制的设计与应用   随着嵌入式系统的发展和广泛应用,必不可少的维护工作变得日益繁重。如移动电话在用户使用过程中,部
    发表于 03-29 15:08 918次阅读
    嵌入式系统自更<b class='flag-5'>新机制</b>的设计与应用

    适用动态存储的自适应刷新机制算法设计

    为满足航天应用中数据传输与存储中高可靠以及低功耗的要求,实现了一种自适应刷新机制的同步动态随机存储(Synchronous Dynamic Random Access MemorySDRAM)控制器
    发表于 04-03 16:00 0次下载
    适用动态存储的自适应<b class='flag-5'>刷新机制</b>算法设计

    B站添加鸿蒙服务卡片教程

    ‍‍‍‍‍‍‍‍ 6 月 2 日鸿蒙发布,今年的六月已经被鸿蒙刷屏了。安卓到鸿蒙,最直观的变化应该就是服务卡片了。我也是在学习
    的头像 发表于 08-12 10:07 2717次阅读
    B站添加<b class='flag-5'>鸿蒙</b>服务<b class='flag-5'>卡片</b>教程

    如何在鸿蒙系统弄一个彩票查询卡片

    接触鸿蒙开发已经有 3 个来月了,最近开始在看鸿蒙卡片开发。因为之前的
    的头像 发表于 09-06 09:17 2611次阅读