当前位置: 首页 > article >正文

鸿蒙应用开发-在Worker中实时修改页面数据(在不同线程中修改主线程中的UI界面)

文章目录

    • 功能概要说明
      • 功能说明
      • 难点
    • 前置条件
      • 宿主线程中使用了AppStorageV2来管理应用UI状态
      • Worker已经创建好了
    • 解决问题
      • 思路
      • 具体实施
        • 封装emitter,让它变得好用一些
        • 在应用的生命周期函数onWindowStageCreate中使用emitter订阅“Setting修改事件”;并开启Worker;处理Worker与UI状态的同步。
        • 在页面中直接使用Setting Store就可以了

功能概要说明

提示:同时适用OpenHarmony和HarmonyOS;版本5.0.0中正常,其他版本没有试过。

功能说明

我们在做一个OpenHarmony应用程序,其中需要实现一个功能,需要用Worker监听几个固件设备采集的数据,然后将采集来的数据实时展示在界面中。

难点

Worker 有自己独立的执行上下文,它和主线程的上下文是隔离的。这句话就意味着,你在Worker中使用的全局变量、单例类以及类中定义的静态属性都是分开的。

前置条件

提示:以下是目前已经拥有的功能或能力

宿主线程中使用了AppStorageV2来管理应用UI状态

不了解AppStorageV2功能的可以查看官方文档:AppStorageV2: 应用全局UI状态存储

//SettingModel.ets
@ObservedV2
export default class Setting {
  @Trace showCompletedTask: boolean=false  //设备是否工作完成,默认未完成
}
//store.ets   获取并初始化Setting的工具方法
//以下代码的意思是:全局获取名字为Setting的store,如果存在就直接获取来,不存在就new Setting(),后面的!表示一定能拿到Setting这个store(加上!是为了在界面中应用时报类型错误,  Setting|undefined 与 Setting类型不匹配)
export const useSettingStore =
  () => AppStorageV2.connect(Setting, 'Setting', () => new Setting())!;
//SettingNavD.ets 组件,这是一个简单的显示状态的按钮
import Setting  from "../../model/SettingModel";
import { useSettingStore } from "../../store";

@ComponentV2
export struct SettingNavD {
  @Local setting: Setting = useSettingStore();
  build() {
    NavDestination() {
      Column() {
        Text('设置')
          .fontSize(40)
          .margin({ bottom: 10 })
        Row() {
          Text('显示已完成任务');
          Toggle({ type: ToggleType.Switch, isOn: this.setting.showCompletedTask })
            .onChange((isOn) => {
              this.setting.showCompletedTask = isOn;
            })
        }
      }
      .alignItems(HorizontalAlign.Start)
    }
  }
}

以上代码可以在宿主线程中通过 useSettingStore()获取到settingStore,修改settingStore的showCompletedTask值,UI就会跟着变化。

Worker已经创建好了

不了解Worker功能的可以查看官方文档:Worker简介

import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
import { JobTypeEnum } from '../service/utils/constant/LfConstantType';
import { LfJob } from './LfJob';
import { EmitterUtil } from '../views/utils/EmitterUtil';
import Setting from '../views/model/SettingModel';

const workerPort: ThreadWorkerGlobalScope = worker.workerPort;


/**
 * Defines the event handler to be called when the worker thread receives a message sent by the host thread.
 * The event handler is executed in the worker thread.
 *
 * @param event message data
 */
workerPort.onmessage = (event: MessageEvents) => {
  const data: JobTypeEnum = event.data;
  try {
    let id: number = 0;
    switch (data) {
      case JobTypeEnum.SYNC_DATA:
        const setting = new Setting()
        let i = 0;
        id = setInterval(() => {
        // 这里会通过采集设备数据,然后修改宿主线程的UI状态,假设每五秒修改一次设置
          setting.showCompletedTask = !setting.showCompletedTask
          EmitterUtil.sendData<Setting>(1, setting)//***********这句就是用来同步Worker与宿主线程中UI状态的代码***********
          // 执行任务
          console.log('数据同步');
        }, 1000 * 5);


        break;
      case JobTypeEnum.COLL_DATA:
         id = setInterval(() => {
           // 执行任务
           console.log('数据采集');
         }, 1000 * 5);
        break;
    }
    if (id !== undefined && id !== null) {
      LfJob.addInterval(data, id);
    }
  } catch (error) {
    workerPort.postMessage({ type: 'error', message: error })
  }
};

/**
 * Defines the event handler to be called when the worker receives a message that cannot be deserialized.
 * The event handler is executed in the worker thread.
 *
 * @param event message data
 */
workerPort.onmessageerror = (event: MessageEvents) => {
};

/**
 * Defines the event handler to be called when an exception occurs during worker execution.
 * The event handler is executed in the worker thread.
 *
 * @param event error message
 */
workerPort.onerror = (event: ErrorEvent) => {
};

解决问题

思路

通过使用Emitter进行线程间通信来解决Worker与宿主进程上下文分离的问题。

  1. 首先在应用的生命周期函数onWindowStageCreate中使用emitter订阅“Setting修改事件”
  2. 然后在worker中修改了Setting后,将修改后的Setting通过emitter进行发布
  3. 当定于的“Setting修改事件”触发后,将得到的新的Setting数据同步给AppStorageV2中的Setting Store,所有使用到Setting Store页面就会跟着变化了。

具体实施

封装emitter,让它变得好用一些
import emitter from '@ohos.events.emitter';

export class EmitterUtil {
  static sendData<T extends object>(eventId: number, data: T) {
    const eventData: emitter.EventData = {
      data
    }
    let innerEvent: emitter.InnerEvent = {
      eventId,
      priority: emitter.EventPriority.HIGH
    };
    emitter.emit(innerEvent, eventData)
  }

  static receiveData<T>(eventId: number, callback: (data: T) => void) {
    let innerEvent: emitter.InnerEvent = {
      eventId,
    };
    emitter.on(innerEvent, (eventData: emitter.EventData) => {
      const data = eventData.data as T
      callback(data)
    })
  }
}

在应用的生命周期函数onWindowStageCreate中使用emitter订阅“Setting修改事件”;并开启Worker;处理Worker与UI状态的同步。
  onWindowStageCreate(windowStage: window.WindowStage): void {
    // 订阅Setting变化的监听器
    EmitterUtil.receiveData<Setting>(1, (setting) => {
    //******* 同步从worker中获取到的setting,给全局Setting Store ********
      const settingStore = useSettingStore()
      settingStore.showCompletedTask = setting.showCompletedTask
    })
    //初始化Worker任务
    if (LfConstant.execJobs.length > 0) {
      for (let job of LfConstant.execJobs) {
        LfJob.getWorker().postMessage(job);
      }
    }
		// 指定主页面入口
    windowStage.loadContent('views/pages/Index', (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.');
    });
  }
在页面中直接使用Setting Store就可以了
import { useSettingStore } from "../../store";

@ComponentV2
export struct SettingNavD {
  @Local setting: Setting = useSettingStore();
  build() {
    NavDestination() {
      Column() {
        Text('设置')
          .fontSize(40)
          .margin({ bottom: 10 })
        Row() {
          Text('显示已完成任务');
          Toggle({ type: ToggleType.Switch, isOn: this.setting.showCompletedTask })
            .onChange((isOn) => {
              this.setting.showCompletedTask = isOn;
            })
        }
      }
      .alignItems(HorizontalAlign.Start)
    }
  }
}

http://www.kler.cn/a/549572.html

相关文章:

  • redis分页查询
  • Postman桌面版登录问题use authorization token to sign in
  • 2010年下半年软件设计师考试上午真题的知识点整理(附真题及答案解析)
  • 【Kubernetes】k8s 部署指南
  • 在Mac arm架构终端中运行 corepack enable yarn 命令,安装yarn
  • 力扣做题记录 (二叉树)
  • 使用redis实现与flink窗口同样的消息聚合处理效果
  • GitLab CI/CD 的配置详解:从零开始使用 .gitlab-ci.yml 文件
  • BEV:车轮接地点车辆修正3D框位置精度
  • OpenCV机器学习(2)提升算法类cv::ml::Boost
  • 第25周Java主流框架实战-springboot入门 4.配置详解
  • 第 16 天:游戏 UI(UMG)开发,打造主菜单 血条!
  • ​矩阵元素的“鞍点”​
  • newgrp docker需要每次刷新问题
  • 使用bitnamiredis-sentinel部署Redis 哨兵模式
  • Android 13 通过修改 AOSP 禁用扬声器
  • 练习题 - DRF 3.x Parsers 解析器使用示例和配置方法
  • openGauss 3.0 数据库在线实训课程16:学习逻辑结构:表管理4
  • R 语言科研绘图第 24 期 --- 直方图-高亮
  • Vue CLI 配置与插件