「鸿蒙学习笔记」Stage模型--服务卡片

「鸿蒙学习笔记」Stage模型--服务卡片

服务卡片(以下简称“卡片”)是一种界面展示形式,可以将应用的重要信息或操作前置到卡片,以达到服务直达、减少体验层级的目的。卡片常用于嵌入到其他应用(当前卡片使用方只支持系统应用,如桌面)中作为其界面显示的一部分,并支持拉起页面、发送消息等基础的交互功能。

服务卡片架构:
「鸿蒙学习笔记」Stage模型--服务卡片_第1张图片

卡片的基本概念:

  • 卡片使用方:如上图中的桌面,显示卡片内容的宿主应用,控制卡片在宿主中展示的位置。
    • 应用图标:应用入口图标,点击后可拉起应用进程,图标内容不支持交互。
    • 卡片:具备不同规格大小的界面展示,卡片的内容可以进行交互,如实现按钮进行界面的刷新、应用的跳转等。
  • 卡片提供方:包含卡片的应用,提供卡片的显示内容、控件布局以及控件点击处理逻辑。
    • FormExtensionAbility:卡片业务逻辑模块,提供卡片创建、销毁、刷新等生命周期回调。
    • 卡片页面:卡片UI模块,包含页面控件、布局、事件等显示和交互信息。

卡片的常见使用步骤如下:
「鸿蒙学习笔记」Stage模型--服务卡片_第2张图片

卡片运行机制

ArkTS卡片实现原理:
「鸿蒙学习笔记」Stage模型--服务卡片_第3张图片

  • 卡片使用方:显示卡片内容的宿主应用,控制卡片在宿主中展示的位置,当前仅系统应用可以作为卡片使用方。
  • 卡片提供方:提供卡片显示内容的应用,控制卡片的显示内容、控件布局以及控件点击事件。
  • 卡片管理服务:用于管理系统中所添加卡片的常驻代理服务,提供formProvider接口能力,同时提供卡片对象的管理与使用以及卡片周期性刷新等能力。
  • 卡片渲染服务:用于管理卡片渲染实例,渲染实例与卡片使用方上的卡片组件一一绑定。卡片渲染服务运行卡片页面代码widgets.abc进行渲染,并将渲染后的数据发送至卡片使用方对应的卡片组件。

ArkTS卡片渲染服务运行原理:
「鸿蒙学习笔记」Stage模型--服务卡片_第4张图片
ArkTS卡片支持在卡片中运行逻辑代码,为确保ArkTS卡片发生问题后不影响卡片使用方应用的使用,ArkTS卡片新增了卡片渲染服务用于运行卡片页面代码widgets.abc,卡片渲染服务由卡片管理服务管理。

卡片使用方的每个卡片组件都对应了卡片渲染服务里的一个渲染实例,同一应用提供方的渲染实例运行在同一个虚拟机运行环境中,不同应用提供方的渲染实例运行在不同的虚拟机运行环境中,通过虚拟机运行环境隔离不同应用提供方卡片之间的资源与状态。

开发过程中需要注意的是globalThis对象的使用,相同应用提供方的卡片globalThis对象是同一个,不同应用提供方的卡片globalThis对象是不同的。

ArkTS卡片的优势:

  • 新增了动效的能力:ArkTS卡片开放了属性动画和显式动画的能力,使卡片的交互更加友好。
  • 新增了自定义绘制的能力:ArkTS卡片开放了Canvas画布组件,卡片可以使用自定义绘制的能力构建更多样的显示和交互效果。
  • 允许卡片中运行逻辑代码:开放逻辑代码运行后很多业务逻辑可以在卡片内部自闭环,拓宽了卡片的业务适用场景。

ArkTS卡片的约束:

  • 不支持加载so。
  • 不支持使用native语言开发。
  • 仅支持声明式范式的部分组件、事件、动效、数据管理、状态管理和API能力。
  • 卡片的事件处理和使用方的事件处理是独立的,建议在使用方支持左右滑动的场景下卡片内容不要使用左右滑动功能的组件,以防手势冲突影响交互体验。
  • 暂不支持导入模块。
  • 暂不支持极速预览。
  • 暂不支持断点调试能力。
  • 暂不支持Hot Reload热重载。

卡片相关模块

「鸿蒙学习笔记」Stage模型--服务卡片_第5张图片

  • FormExtensionAbility:卡片扩展模块,提供卡片创建、销毁、刷新等生命周期回调。
  • FormExtensionContext:FormExtensionAbility的上下文环境,提供FormExtensionAbility具有的接口和能力。
  • formProvider:提供卡片提供方相关的接口能力,可通过该模块提供接口实现更新卡片、设置卡片更新时间、获取卡片信息、请求发布卡片等。
  • formInfo:提供了卡片信息和状态等相关类型和枚举。
  • formBindingData:提供卡片数据绑定的能力,包括FormBindingData对象的创建、相关信息的描述。
  • 页面布局(Card.ets):提供声明式范式的UI接口能力。
    • ArkTS卡片特有能力:postCardAction用于卡片内部和提供方应用间的交互,仅在卡片中可以调用。
    • ArkTS卡片能力列表:列举了能在ArkTS卡片中使用的API、组件、事件、属性和生命周期调度。
  • 卡片配置:包含FormExtensionAbility的配置和卡片的配置
    • 在module.json5配置文件中的extensionAbilities标签下,配置FormExtensionAbility相关信息。
    • 在resources/base/profile/目录下的form_config.json配置文件中,配置卡片(WidgetCard.ets)相关信息。

创建卡片

创建卡片:
「鸿蒙学习笔记」Stage模型--服务卡片_第6张图片

ArkTS卡片创建完成后,工程中会新增如下卡片相关文件:卡片生命周期管理文件(EntryFormAbility.ts)、卡片页面文件(WidgetCard.ets)和卡片配置文件(form_config.json)。
「鸿蒙学习笔记」Stage模型--服务卡片_第7张图片

配置卡片

卡片相关的配置文件主要包含FormExtensionAbility的配置和卡片的配置两部分:

  1. 卡片需要在module.json5配置文件中的extensionAbilities标签下,配置FormExtensionAbility相关信息。FormExtensionAbility需要填写metadata元信息标签,其中键名称为固定字符串“ohos.extension.form”,资源为卡片的具体配置信息的索引。

    {
      "module": {
     ...
     "extensionAbilities": [
       {
         "name": "EntryFormAbility",
         "srcEntrance": "./ets/entryformability/EntryFormAbility.ts",
         "label": "$string:EntryFormAbility_label",
         "description": "$string:EntryFormAbility_desc",
         "type": "form",
         "metadata": [
           {
             "name": "ohos.extension.form",
             "resource": "$profile:form_config"
           }
         ]
       }
     ]
      }
    }
  2. 卡片的具体配置信息。在上述FormExtensionAbility的元信息(“metadata”配置项)中,可以指定卡片具体配置信息的资源索引。例如当resource指定为$profile:form_config时,会使用开发视图的resources/base/profile/目录下的form_config.json作为卡片profile配置文件。

    {
      "forms": [
     {
       "name": "widget",
       "description": "This is a service widget.",
       "src": "./ets/widget/pages/WidgetCard.ets",
       "uiSyntax": "arkts",
       "window": {
         "designWidth": 720,
         "autoDesignWidth": true
       },
       "colorMode": "auto",
       "isDefault": true,
       "updateEnabled": true,
       "scheduledUpdateTime": "10:30",
       "updateDuration": 1,
       "defaultDimension": "2*2",
       "supportDimensions": [
         "2*2"
       ]
     }
      ]
    }

卡片生命周期管理

创建ArkTS卡片,需实现FormExtensionAbility生命周期接口。

  1. 在EntryFormAbility.ts中,导入相关模块。

    import formInfo from '@ohos.app.form.formInfo';
    import formBindingData from '@ohos.app.form.formBindingData';
    import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
    import formProvider from '@ohos.app.form.formProvider';
  2. 在EntryFormAbility.ts中,实现FormExtensionAbility生命周期接口,其中在onAddForm的入参want中可以通过FormParam取出卡片的相关信息。

    import formInfo from '@ohos.app.form.formInfo';
    import formBindingData from '@ohos.app.form.formBindingData';
    import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
    import formProvider from '@ohos.app.form.formProvider';
    
    export default class EntryFormAbility extends FormExtensionAbility {
      onAddForm(want) {
     console.info('[EntryFormAbility] onAddForm');
     // 在入参want中可以取出卡片的唯一标识:formId
     let formId: string = want.parameters[formInfo.FormParam.IDENTITY_KEY];
     // 使用方创建卡片时触发,提供方需要返回卡片数据绑定类
     let obj = {
       'title': 'titleOnAddForm',
       'detail': 'detailOnAddForm'
     };
     let formData = formBindingData.createFormBindingData(obj);
     return formData;
      }
    
      onCastToNormalForm(formId) {
     // Called when the form provider is notified that a temporary form is successfully
     // converted to a normal form.
     // 使用方将临时卡片转换为常态卡片触发,提供方需要做相应的处理
     console.info(`[EntryFormAbility] onCastToNormalForm, formId: ${formId}`);
      }
    
      onUpdateForm(formId) {
     // 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新
     console.info('[EntryFormAbility] onUpdateForm');
     let obj = {
       'title': 'titleOnUpdateForm',
       'detail': 'detailOnUpdateForm'
     };
     let formData = formBindingData.createFormBindingData(obj);
     formProvider.updateForm(formId, formData).catch((err) => {
       if (err) {
         // 异常分支打印
         console.error(`[EntryFormAbility] Failed to updateForm. Code: ${err.code}, message: ${err.message}`);
         return;
       }
     });
      }
    
      onChangeFormVisibility(newStatus) {
     // Called when the form provider receives form events from the system.
     // 需要配置formVisibleNotify为true,且为系统应用才会回调
     console.info('[EntryFormAbility] onChangeFormVisibility');
      }
    
      onFormEvent(formId, message) {
     // Called when a specified message event defined by the form provider is triggered.
     // 若卡片支持触发事件,则需要重写该方法并实现对事件的触发
     console.info('[EntryFormAbility] onFormEvent');
      }
    
      onRemoveForm(formId) {
     // Called to notify the form provider that a specified form has been destroyed.
     // 当对应的卡片删除时触发的回调,入参是被删除的卡片ID
     console.info('[EntryFormAbility] onRemoveForm');
      }
    
      onConfigurationUpdate(config) {
     // 当系统配置信息置更新时触发的回调
     console.info('[EntryFormAbility] configurationUpdate:' + JSON.stringify(config));
      }
    
      onAcquireFormState(want) {
     // Called to return a {@link FormState} object.
     // 卡片提供方接收查询卡片状态通知接口,默认返回卡片初始状态。
     return formInfo.FormState.READY;
      }
    }

开发卡片事件

ArkTS卡片中提供了postCardAction()接口用于卡片内部和提供方应用间的交互,当前支持router、message和call三种类型的事件,仅在卡片中可以调用。
「鸿蒙学习笔记」Stage模型--服务卡片_第8张图片

使用router事件跳转到指定UIAbility

在卡片中使用postCardAction接口的router能力,能够快速拉起卡片提供方应用的指定UIAbility,因此UIAbility较多的应用往往会通过卡片提供不同的跳转按钮,实现一键直达的效果。例如相机卡片,卡片上提供拍照、录像等按钮,点击不同按钮将拉起相机应用的不同UIAbility,从而提升用户的体验。
「鸿蒙学习笔记」Stage模型--服务卡片_第9张图片

通常使用按钮控件来实现页面拉起,示例代码如下:

  • 在卡片页面中布局两个按钮,点击其中一个按钮时调用postCardAction向指定UIAbility发送router事件,并在事件内定义需要传递的内容。

    @Entry
    @Component
    struct WidgetCard {
    build() {
      Column() {
        Button('功能A')
          .margin('20%')
          .onClick(() => {
            console.info('Jump to EntryAbility funA');
            postCardAction(this, {
              'action': 'router',
              'abilityName': 'EntryAbility', // 只能跳转到当前应用下的UIAbility
              'params': {
                'targetPage': 'funA' // 在EntryAbility中处理这个信息
              }
            });
          })
    
        Button('功能B')
          .margin('20%')
          .onClick(() => {
            console.info('Jump to EntryAbility funB');
            postCardAction(this, {
              'action': 'router',
              'abilityName': 'EntryAbility', // 只能跳转到当前应用下的UIAbility
              'params': {
                'targetPage': 'funB' // 在EntryAbility中处理这个信息
              }
            });
          })
      }
      .width('100%')
      .height('100%')
    }
    }
  • 在UIAbility中接收router事件并获取参数,根据传递的message不同,选择拉起不同的页面。

    import UIAbility from '@ohos.app.ability.UIAbility';
    import window from '@ohos.window';
    
    let selectPage = "";
    let currentWindowStage = null;
    
    export default class CameraAbility extends UIAbility {
    // 如果UIAbility第一次启动,在收到Router事件后会触发onCreate生命周期回调
    onCreate(want, launchParam) {
      // 获取router事件中传递的targetPage参数
      console.info("onCreate want:" + JSON.stringify(want));
      if (want.parameters.params !== undefined) {
        let params = JSON.parse(want.parameters.params);
        console.info("onCreate router targetPage:" + params.targetPage);
        selectPage = params.targetPage;
      }
    }
    // 如果UIAbility已在后台运行,在收到Router事件后会触发onNewWant生命周期回调
    onNewWant(want, launchParam) {
      console.info("onNewWant want:" + JSON.stringify(want));
      if (want.parameters.params !== undefined) {
        let params = JSON.parse(want.parameters.params);
        console.info("onNewWant router targetPage:" + params.targetPage);
        selectPage = params.targetPage;
      }
      if (currentWindowStage != null) {
        this.onWindowStageCreate(currentWindowStage);
      }
    }
    
    onWindowStageCreate(windowStage: window.WindowStage) {
      let targetPage;
      // 根据传递的targetPage不同,选择拉起不同的页面
      switch (selectPage) {
        case 'funA':
          targetPage = 'pages/FunA';
          break;
        case 'funB':
          targetPage = 'pages/FunB';
          break;
        default:
          targetPage = 'pages/Index';
      }
      if (currentWindowStage === null) {
        currentWindowStage = windowStage;
      }
      windowStage.loadContent(targetPage, (err, data) => {
        if (err && err.code) {
          console.info('Failed to load the content. Cause: %{public}s', JSON.stringify(err));
          return;
        }
      });
    }
    };

使用call事件拉起指定UIAbility到后台

许多应用希望借助卡片的能力,实现和应用在前台时相同的功能。例如音乐卡片,卡片上提供播放、暂停等按钮,点击不同按钮将触发音乐应用的不同功能,进而提高用户的体验。在卡片中使用postCardAction接口的call能力,能够将卡片提供方应用的指定UIAbility拉到后台。同时,call能力提供了调用应用指定方法、传递数据的功能,使应用在后台运行时可以通过卡片上的按钮执行不同的功能。

通常使用按钮控件来触发call事件,示例代码如下:

  • 在卡片页面中布局两个按钮,点击其中一个按钮时调用postCardAction向指定UIAbility发送call事件,并在事件内定义需要调用的方法和传递的数据。需要注意的是,method参数为必选参数,且类型需要为string类型,用于触发UIAbility中对应的方法。

    @Entry
    @Component
    struct WidgetCard {
    build() {
      Column() {
        Button('功能A')
          .margin('20%')
          .onClick(() => {
            console.info('call EntryAbility funA');
            postCardAction(this, {
              'action': 'call',
              'abilityName': 'EntryAbility', // 只能跳转到当前应用下的UIAbility
              'params': {
                'method': 'funA' // 在EntryAbility中调用的方法名
              }
            });
          })
         Button('功能B')
          .margin('20%')
          .onClick(() => {
            console.info('call EntryAbility funB');
            postCardAction(this, {
              'action': 'call',
              'abilityName': 'EntryAbility', // 只能跳转到当前应用下的UIAbility
              'params': {
                'method': 'funB', // 在EntryAbility中调用的方法名
                'num': 1 // 需要传递的其他参数
              }
            });
          })
      }
      .width('100%')
      .height('100%')
    }
    }
  • 在UIAbility中接收call事件并获取参数,根据传递的method不同,执行不同的方法。其余数据可以通过readString的方式获取。需要注意的是,UIAbility需要onCreate生命周期中监听所需的方法。

    import UIAbility from '@ohos.app.ability.UIAbility';
     
    function FunACall(data) {
    // 获取call事件中传递的所有参数
    console.log('FunACall param:' + JSON.stringify(data.readString()));
    return null;
    }
     function FunBCall(data) {
    console.log('FunACall param:' + JSON.stringify(data.readString()));
    return null;
    }
     
    export default class CameraAbility extends UIAbility {
    // 如果UIAbility第一次启动,在收到call事件后会触发onCreate生命周期回调
    onCreate(want, launchParam) {
        try {
            // 监听call事件所需的方法
            this.callee.on('funA', FunACall);
            this.callee.on('funB', FunBCall);
        } catch (error) {
            console.log('register failed with error. Cause: ' + JSON.stringify(error));
        }
    }
     
    // 进程退出时,解除监听
    onDestroy() {
        try {
            this.callee.off('funA');
            this.callee.off('funB');
        } catch (error) {
            console.log('register failed with error. Cause: ' + JSON.stringify(error));
        }
    }
    };

通过message事件刷新卡片内容

在卡片页面中可以通过postCardAction接口触发message事件拉起FormExtensionAbility,然后由FormExtensionAbility刷新卡片内容,下面是这种刷新方式的简单示例。

  • 在卡片页面通过注册Button的onClick点击事件回调,并在回调中调用postCardAction接口触发事件至FormExtensionAbility

    let storage = new LocalStorage();
    
    @Entry(storage)
    @Component
    struct WidgetCard {
    @LocalStorageProp('title') title: string = 'init';
    @LocalStorageProp('detail') detail: string = 'init';
    
    build() {
      Column() {
        Button('刷新')
          .onClick(() => {
            postCardAction(this, {
              'action': 'message',
              'params': {
                'msgTest': 'messageEvent'
              }
            });
          })
        Text(`${this.title}`)
        Text(`${this.detail}`)
      }
      .width('100%')
      .height('100%')
    }
    }
  • 在FormExtensionAbility的onFormEvent生命周期中调用updateForm接口刷新卡片

    import formBindingData from '@ohos.app.form.formBindingData';
    import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
    import formProvider from '@ohos.app.form.formProvider';
    
    export default class EntryFormAbility extends FormExtensionAbility {
    onFormEvent(formId, message) {
      // Called when a specified message event defined by the form provider is triggered.
      console.info(`FormAbility onEvent, formId = ${formId}, message: ${JSON.stringify(message)}`);
      let formData = {
        'title': 'Title Update Success.', // 和卡片布局中对应
        'detail': 'Detail Update Success.', // 和卡片布局中对应
      };
      let formInfo = formBindingData.createFormBindingData(formData)
      formProvider.updateForm(formId, formInfo).then((data) => {
        console.info('FormAbility updateForm success.' + JSON.stringify(data));
      }).catch((error) => {
        console.error('FormAbility updateForm failed: ' + JSON.stringify(error));
      })
    }
    
    ...
    }

    效果如图:
    「鸿蒙学习笔记」Stage模型--服务卡片_第10张图片

通过router或call事件刷新卡片内容

在卡片页面中可以通过postCardAction接口触发router或call事件拉起UIAbility,然后由UIAbility刷新卡片内容,下面是这种刷新方式的简单示例。

通过router事件刷新卡片内容
  • 在卡片页面通过注册Button的onClick点击事件回调,并在回调中调用postCardAction接口触发router事件至FormExtensionAbility。

    let storage = new LocalStorage();
    
    @Entry(storage)
    @Component
    struct WidgetCard {
    @LocalStorageProp('detail') detail: string = 'init';
    
    build() {
      Column() {
        Button('跳转')
          .margin('20%')
          .onClick(() => {
            console.info('postCardAction to EntryAbility');
            postCardAction(this, {
              'action': 'router',
              'abilityName': 'EntryAbility', // 只能跳转到当前应用下的UIAbility
              'params': {
                'detail': 'RouterFromCard'
              }
            });
          })
        Text(`${this.detail}`).margin('20%')
      }
      .width('100%')
      .height('100%')
    }
    }
  • 在UIAbility的onCreate()或者onNewWant()生命周期中可以通过入参want获取卡片的formID和传递过来的参数信息,然后调用updateForm接口刷新卡片。

    import UIAbility from '@ohos.app.ability.UIAbility';
    import formBindingData from '@ohos.app.form.formBindingData';
    import formProvider from '@ohos.app.form.formProvider';
    import formInfo from '@ohos.app.form.formInfo';
    
    export default class EntryAbility extends UIAbility {
    // 如果UIAbility第一次启动,在收到Router事件后会触发onCreate生命周期回调
    onCreate(want, launchParam) {
      console.info('Want:' + JSON.stringify(want));
      if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) {
        let curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
        let message = JSON.parse(want.parameters.params).detail;
        console.info(`UpdateForm formId: ${curFormId}, message: ${message}`);
        let formData = {
          "detail": message + ': onCreate UIAbility.', // 和卡片布局中对应
        };
        let formMsg = formBindingData.createFormBindingData(formData)
        formProvider.updateForm(curFormId, formMsg).then((data) => {
          console.info('updateForm success.' + JSON.stringify(data));
        }).catch((error) => {
          console.error('updateForm failed:' + JSON.stringify(error));
        })
      }
    }
    // 如果UIAbility已在后台运行,在收到Router事件后会触发onNewWant生命周期回调
    onNewWant(want, launchParam) {
      console.info('onNewWant Want:' + JSON.stringify(want));
      if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) {
        let curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
        let message = JSON.parse(want.parameters.params).detail;
        console.info(`UpdateForm formId: ${curFormId}, message: ${message}`);
        let formData = {
          "detail": message + ': onNewWant UIAbility.', // 和卡片布局中对应
        };
        let formMsg = formBindingData.createFormBindingData(formData)
        formProvider.updateForm(curFormId, formMsg).then((data) => {
          console.info('updateForm success.' + JSON.stringify(data));
        }).catch((error) => {
          console.error('updateForm failed:' + JSON.stringify(error));
        })
      }
    }
    
    ...
    }
通过call事件刷新卡片内容
  • 在使用postCardAction接口的call事件时,需要在FormExtensionAbility中的onAddForm生命周期回调中更新formId。

    import formBindingData from '@ohos.app.form.formBindingData'; 
    import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
    
    export default class EntryFormAbility extends FormExtensionAbility {
    onAddForm(want) {
     let formId = want.parameters["ohos.extra.param.key.form_identity"];
     let dataObj1 = {
       "formId": formId
     };
     let obj1 = formBindingData.createFormBindingData(dataObj1);
     return obj1;
     }
      
     ...
    };
  • 在卡片页面通过注册Button的onClick点击事件回调,并在回调中调用postCardAction接口触发call事件至UIAbility。

    let storage = new LocalStorage();
    
    @Entry(storage)
    @Component
    struct WidgetCard {
    @LocalStorageProp('detail') detail: string = 'init';
    @LocalStorageProp('formId') formId: string = '0';
    
    build() {
      Column() {
        Button('拉至后台')
          .margin('20%')
          .onClick(() => {
            console.info('postCardAction to EntryAbility');
            postCardAction(this, {
              'action': 'call',
              'abilityName': 'EntryAbility', // 只能跳转到当前应用下的UIAbility
              'params': {
                'method': 'funA',
                'formId': this.formId,
                'detail': 'CallFromCard'
              }
            });
          })
        Text(`${this.detail}`).margin('20%')
      }
      .width('100%')
      .height('100%')
    }
    }
  • 在UIAbility的onCreate生命周期中监听call事件所需的方法,然后调用updateForm接口刷新卡片。

    import UIAbility from '@ohos.app.ability.UIAbility';
    import formBindingData from '@ohos.app.form.formBindingData';
    import formProvider from '@ohos.app.form.formProvider';
    import formInfo from '@ohos.app.form.formInfo';
    const MSG_SEND_METHOD: string = 'funA'
     
    // 在收到call事件后会触发callee监听的方法
    function FunACall(data) {
    // 获取call事件中传递的所有参数
    let params = JSON.parse(data.readString())
    if (params.formId !== undefined) {
      let curFormId = params.formId;
      let message = params.detail;
      console.info(`UpdateForm formId: ${curFormId}, message: ${message}`);
      let formData = {
        "detail": message
      };
      let formMsg = formBindingData.createFormBindingData(formData)
      formProvider.updateForm(curFormId, formMsg).then((data) => {
        console.info('updateForm success.' + JSON.stringify(data));
      }).catch((error) => {
        console.error('updateForm failed:' + JSON.stringify(error));
      })
    }
    return null;
    }
    export default class EntryAbility extends UIAbility {
    // 如果UIAbility第一次启动,call事件后会触发onCreate生命周期回调
    onCreate(want, launchParam) {
      console.info('Want:' + JSON.stringify(want));
      try {
         // 监听call事件所需的方法
        this.callee.on(MSG_SEND_METHOD, FunACall);
      } catch (error) {
        console.log(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`)
      }
    }
    ...
    }

卡片数据交互

ArkTS卡片框架提供了 updateForm() 接口和 requestForm() 接口主动触发卡片的页面刷新。
「鸿蒙学习笔记」Stage模型--服务卡片_第11张图片

当前卡片框架提供了如下几种刷新卡片的方式:

  • 定时刷新:表示在一定时间间隔内调用onUpdateForm的生命周期回调函数自动刷新卡片内容。可以在form_config.json配置文件的updateDuration字段中进行设置。例如,可以将刷新时间设置为每小时一次。注意:updateDuration(定时刷新)优先级比scheduledUpdateTime(定点刷新)高,配置定时刷新后,定点刷新将失效。

    {
    "forms": [
      {
        "name": "widget",
        "description": "This is a service widget.",
        "src": "./ets/widget/pages/WidgetCard.ets",
        "uiSyntax": "arkts",
        "window": {
          "designWidth": 720,
          "autoDesignWidth": true
        },
        "colorMode": "auto",
        "isDefault": true,
        "updateEnabled": true, // 使能刷新功能
        "scheduledUpdateTime": "10:30",                               
        "updateDuration": 2, // 设置卡片定时刷新的更新周期(单位为30分钟,取值为自然数)
        "defaultDimension": "2*2",
        "supportDimensions": ["2*2"]
      }
    ]
    }
  • 定点刷新:表示每天在某个时间点刷新,在form_config.json文件中配置,详见scheduledUpdateTime字段。例如,每天在10:30更新卡片内容。

    {
    "forms": [
      {
        "name": "widget",
        "description": "This is a service widget.",
        "src": "./ets/widget/pages/WidgetCard.ets",
        "uiSyntax": "arkts",
        "window": {
          "designWidth": 720,
          "autoDesignWidth": true
        },
        "colorMode": "auto",
        "isDefault": true,
        "updateEnabled": true, // 使能刷新功能
        "scheduledUpdateTime": "10:30", // 设置卡片的定点刷新的时刻
        "updateDuration": 0,
        "defaultDimension": "2*2",
        "supportDimensions": ["2*2"]
      }
    ]
    }
    当同时配置了定时刷新(updateDuration)和定点刷新(scheduledUpdateTime)时,定时刷新的优先级更高。如果想要配置定点刷新,则需要将updateDuration配置为0。
  • 下次刷新:通过setFormNextRefreshTime接口指定卡片的下一次刷新时间(最短时间5分钟),例如,在接口调用的5分钟后刷新卡片内容。

    import formProvider from '@ohos.app.form.formProvider';
    
    let formId = '123456789'; // 实际业务场景需要使用正确的formId
    try {
    // 设置过5分钟后更新卡片内容
    formProvider.setFormNextRefreshTime(formId, 5, (err, data) => {
      if (err) {
        console.error(`Failed to setFormNextRefreshTime. Code: ${err.code}, message: ${err.message}`);
        return;
      } else {
        console.info('Succeeded in setFormNextRefreshTimeing.');
      }
    });
    } catch (err) {
    console.error(`Failed to setFormNextRefreshTime. Code: ${err.code}, message: ${err.message}`);
    }

在触发定时、定点或主动刷新后,系统会调用FormExtensionAbility的onUpdateForm生命周期回调,在回调中,可以使用updateForm进行提供方刷新卡片。onUpdateForm生命周期回调参考通过FormExtensionAbility刷新卡片内容

  1. 定时刷新有配额限制,每张卡片每天最多通过定时方式触发刷新50次,定时刷新包含卡片配置项updateDuration和调用setFormNextRefreshTime两种,当达到50次配额后,无法通过定时方式再次触发刷新,刷新次数会在每天的0点重置。
  2. 当前定时刷新使用同一个计时器进行计时,因此卡片定时刷新的第一次刷新会有最多30分钟的偏差。比如第一张卡片A(每隔半小时刷新一次)在3点20分添加成功,定时器启动并每隔半小时触发一次事件,第二张卡片B(每隔半小时刷新一次)在3点40分添加成功,在3点50分定时器事件触发时,卡片A触发定时刷新,卡片B会在下次事件(4点20分)中才会触发。
  3. 定时刷新和定点刷新仅在屏幕亮屏情况下才会触发,在灭屏场景下仅会将记录刷新动作,待亮屏时统一进行刷新。

参考

你可能感兴趣的:(harmonyos)