桌面卡片开发教程:从底层原理开始讲透call事件的刷新机制

首先铺垫两个基础知识:

 

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

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

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

每张卡片都有一个独立的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的卡片检测并同步到数据变动,完成页面变更。

     

postCardAction接口call事件的写法如下

postCardAction%E6%8E%A5%E5%8F%A3.png

action%E5%8F%82%E6%95%B0.png

 

updateForm接口的写法如下:

updataForm.png

 

 

上案例代码:

 

  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;
     }
    };复制

    cke_2673013.gif

  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
                }
              });
            })
      }
    }复制

    cke_2758838.gif

     

  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%')
      }
    }复制

    进入华为专区,解锁更多精彩内容

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值