HarmonyOS鸿蒙Next中桌面卡片修改数据后如何触发应用内同步

HarmonyOS鸿蒙Next中桌面卡片修改数据后如何触发应用内同步 应用处于后台

桌面卡片点击通过postCardAction发送了message事件

在EntryFormAbility的onFormEvent回调中收到事件

然后将修改的数据提交到了服务器

此时如何触发应用页面相关数据的刷新呢?

已解决

4 回复

【分析结论】

1.应用没有在卡片中使用postCardAction接口的call能力,所以卡片进程的数据变化无法同步给主进程。

2.应用页面中展示的数据没有使用全局UI状态存储,并进行双向绑定,导致页面数据无法实时更新。

【解决方案】

1.配置后台运行权限。call事件存在约束限制,卡片提供方应用需要在module.json5下添加后台运行权限。

// src/main/module.json5
"requestPermissions":[
  {
    "name": "ohos.permission.KEEP_BACKGROUND_RUNNING"
  }
]

2.修改EntryFormAbility添加formId

import { formBindingData, FormExtensionAbility, formInfo } from '@kit.FormKit';
import { Want } from '@kit.AbilityKit';

export default class EntryFormAbility extends FormExtensionAbility {
  onAddForm(want: Want) {
 if(!want || !want.parameters) {
   return formBindingData.createFormBindingData('');
 }
 let formId: string = want.parameters[formInfo.FormParam.IDENTITY_KEY] as string;
 let formData: Record<string, string> =  {
   'formId': formId
 };
 return formBindingData.createFormBindingData(formData);
  }

  onCastToNormalForm(formId: string) {
 // Called when the form provider is notified that a temporary form is successfully
 // converted to a normal form.
  }

  onUpdateForm(formId: string) {
 // Called to notify the form provider to update a specified form.
  }

  onFormEvent(formId: string, message: string) {

  }

  onRemoveForm(formId: string) {
 // Called to notify the form provider that a specified form has been destroyed.
  }

  onAcquireFormState(want: Want) {
 // Called to return a {@link FormState} object.
 return formInfo.FormState.READY;
  }
}

3.配置指定的UIAbility,在module.json5的abilities数组内添加对应的配置信息,也可以直接使用EntryAbility。

// src/main/module.json5
"abilities": [
 {
   "name": 'WidgetEventCallEntryAbility',
   "srcEntry": './ets/widgeteventcallcard/WidgetEventCallEntryAbility/WidgetEventCallEntryAbility.ets',
   "description": '$string:WidgetEventCallCard_desc',
   "icon": "$media:app_icon",
   "label": "$string:WidgetEventCallCard_label",
   "startWindowIcon": "$media:app_icon",
   "startWindowBackground": "$color:start_window_background"
 }
]

4.点击卡片更新数据时,在卡片页面调用postCardAction向指定UIAbility发送call事件,并根据开发发过程中的实际需要传参。

let storageUpdateByMsg = new LocalStorage();

@Entry(storageUpdateByMsg)
@Component
struct WidgetExampleCard {
  @LocalStorageProp('title') title: string = 'title';
  @LocalStorageProp('detail') detail: string = '未更新.'
  @LocalStorageProp('formId') formId: string = '';

  build() {
    Row() {
      Column() {
        Text(this.title)
          .fontSize(14)
          .fontWeight(FontWeight.Medium)
          .fontColor($r('sys.color.font'))
        Text(this.detail)
          .fontSize(14)
          .fontWeight(FontWeight.Medium)
          .fontColor($r('sys.color.font'))
        // 卡片按钮点击事件
        Button('更新数据')
          .onClick(() => {
            postCardAction(this, {
              action: 'call',
              // 只能跳转到当前应用下的UIAbility,与module.json5中定义保持一致
              abilityName: 'EntryAbility',
              params: {
                formId: this.formId,
                // 需要调用的方法名称
                method: 'funA',
                newData: 'new title',  // 修改后的数据
                detail: '已更新',
                formEvent: 'DATA_CHANGE' // 自定义事件类型
              }
            });
          })
      }
      .width('100%')
    }
    .height('100%')
    .backgroundColor($r('sys.color.comp_background_primary'))
  }
}

5.在指定的UIAbility中监听call事件,根据监听到的method参数中的方法名称调用对应方法,并通过rpc.Parcelable获取参数,使用AppStorage进行全局的UI状态存储。

import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { rpc } from '@kit.IPCKit';
import { formBindingData, formProvider } from '@kit.FormKit';
import { BusinessError } from '@kit.BasicServicesKit';

const DOMAIN = 0x0000;
// ipc通信返回类型的实现,用于数据序列化和反序列化
class MyParcelable implements rpc.Parcelable {
  num: number;
  str: string;

  constructor(num: number, str: string) {
    this.num = num;
    this.str = str;
  }

  marshalling(messageSequence: rpc.MessageSequence): boolean {
    messageSequence.writeInt(this.num);
    messageSequence.writeString(this.str);
    return true;
  }

  unmarshalling(messageSequence: rpc.MessageSequence): boolean {
    this.num = messageSequence.readInt();
    this.str = messageSequence.readString();
    return true;
  }
}

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
    // 监听call事件所需的方法并调用
    this.callee.on('funA', this.callUpdateFunc);
  }

  private callUpdateFunc = (data: rpc.MessageSequence): MyParcelable => {
    let params: Record<string, string> = JSON.parse(data.readString());
    if (params.formEvent === 'DATA_CHANGE') {
      AppStorage.setOrCreate('title', params.newData);
      AppStorage.setOrCreate('detail', params.detail);
      if (params.formId !== undefined) {
        // 更新卡片显示
        const bindData = formBindingData.createFormBindingData({
          'title': params.newData,
          'detail': params.detail
        });
        formProvider.updateForm(params.formId, bindData).catch((err: BusinessError) => {
          console.error(`更新卡片失败: ${params.formId}, ${err.code}, ${err.message}`);
        });
      }
    }
    return new MyParcelable(1, 'aaa');
  };

  onDestroy(): void {
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');
    try {
      this.callee.off('funA');
    } catch (err) {
      hilog.info(DOMAIN, 'testTag', '%{public}s', JSON.stringify(err));
    }
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
        return;
      }
      AppStorage.setOrCreate('title', 'title');
      AppStorage.setOrCreate('detail', '未更新');
      hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
    });
  }
}

6.在应用页面内使用@StorageLink@StorageProp对AppStorage中对应的属性建立数据同步,使页面数据同步更新。

@Entry
@Component
struct Index {
  [@StorageLink](/user/StorageLink)('title') title: string = '';
  [@StorageLink](/user/StorageLink)('detail') detail: string = '';

  build() {
    Column({space: 20}) {
      Text(`卡片内容:${this.title}`)
        .fontSize(20)
        .margin(10)
      Text(`描述:${this.detail}`)
        .fontSize(20)
        .margin(10)
    }
    .height('100%')
    .width('100%')
    .padding(20)
  }
}

更多关于HarmonyOS鸿蒙Next中桌面卡片修改数据后如何触发应用内同步的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


message事件无法和UI层面通信

需要使用call事件实现

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-ui-widget-event-call

在HarmonyOS Next中,桌面卡片数据修改后,可通过FormKit的FormProvider类更新卡片数据。应用内同步使用postFormUpdate方法,并配合onUpdateForm生命周期回调。卡片通过updateForm主动请求更新,应用接收事件后刷新数据。

在HarmonyOS Next中,当桌面卡片通过postCardAction发送事件,并在EntryFormAbilityonFormEvent回调中完成服务器数据提交后,要触发应用内页面刷新,核心机制是使用Ability上下文的事件通知

由于应用处于后台,无法直接操作UI,你需要通过UIAbility的事件机制来通知页面更新。具体步骤如下:

  1. 在UIAbility中注册事件订阅:在你的主Ability(例如EntryAbility)中,使用UIAbilityContexteventHub对象,定义一个供页面订阅的自定义事件(例如'cardDataUpdate')。

  2. 在onFormEvent中触发事件:在EntryFormAbilityonFormEvent回调函数中,获取UIAbility的上下文,并通过其eventHub触发上一步定义的自定义事件。可以将更新后的数据作为事件参数传递。

  3. 在页面中监听事件:在需要刷新的页面(例如Index.ets)中,通过UIAbilityContexteventHub订阅该自定义事件。在事件回调函数中,执行数据更新和页面刷新逻辑。

关键代码示例

  • 在UIAbility中定义事件(通常可在onCreate中):
// EntryAbility.ets
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  // ... 其他代码
  // 定义事件,供页面订阅
  this.context.eventHub.on('cardDataUpdate', (data: any) => {
    // 此处可进行一些Ability层面的处理,事件主要目的是通知页面
  });
}
  • 在FormAbility中触发事件
// EntryFormAbility.ets
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import { BusinessError } from '@ohos.base';
import common from '@ohos.app.ability.common';

export default class EntryFormAbility extends FormExtensionAbility {
  onFormEvent(formId: string, message: string): void {
    // 1. 处理卡片事件,提交数据到服务器...
    // 2. 服务器提交成功后,触发UIAbility事件以通知页面刷新
    let uiAbilityContext: common.UIAbilityContext = this.context.getUiAbilityContext();
    uiAbilityContext.eventHub.emit('cardDataUpdate', { refreshedData: yourUpdatedData });
    // yourUpdatedData 是从服务器获取或生成的新数据
  }
}
  • 在页面中订阅事件并刷新
// Index.ets
import { UIAbilityContext } from '@ohos.abilityAccess';

@Entry
@Component
struct Index {
  private context = getContext(this) as UIAbilityContext;
  @State data: any = []; // 你的页面数据

  aboutToAppear(): void {
    // 订阅UIAbilityContext发出的事件
    this.context.eventHub.on('cardDataUpdate', (eventData: any) => {
      // 接收到事件后,更新页面数据
      this.data = eventData.refreshedData; // 或执行网络请求重新拉取数据
      // @State变量更新会自动触发UI刷新
    });
  }

  aboutToDisappear(): void {
    // 可选:在页面消失时取消订阅
    this.context.eventHub.off('cardDataUpdate');
  }

  build() {
    // 你的页面UI构建,使用this.data渲染
  }
}

总结:通过UIAbilityContexteventHub,建立了一个从FormExtensionAbility到前台页面的跨Ability通信通道。卡片事件处理完成后,发射一个自定义事件,前台页面监听该事件并执行数据更新,从而实现后台卡片数据修改与前台页面刷新的同步。

回到顶部