HarmonyOS鸿蒙Next中在woker中调用单例对象,到调用对象函数的地方就报not initialized

HarmonyOS鸿蒙Next中在woker中调用单例对象,到调用对象函数的地方就报not initialized

我封装了一个logger类,用于将程序运行的日志写入到本地,最后可以供下载。在实现worker的ets中调用这个logger类实例的时候,就会报not initialized。但是在UI相关的ets文件中调用这个类,就不会报错,可以正常写入。这是啥原因?如何规避?

模拟器版本:

报错截图:

cke_4291.png

关键类SdpLogger实现(末尾定义了一个此类的实例导出用于其他ets调用):

/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { hilog } from '@kit.PerformanceAnalysisKit';
import { common } from '@kit.AbilityKit';
import { fileIo as fs } from '@kit.CoreFileKit';
import { getLogFile } from '../workers/localFile';

const LOG_TAG = 'AppLogger';
export const LOG_ERROR:number = 0;
export const LOG_WARN:number = 1;
export const LOG_INFO:number = 2;
export const LOG_DEBUG:number = 3;
enum LogLevel {
  ERROR = 0, //错误
  WARN = 1,  //警告
  INFO = 2,  //一般
  DEBUG = 3  //调试
}
export class SdpLogger {
  private domain: number;
  private prefix: string;
  private format: string = '%{public}s';
  private logFile:string = '';
  private logLevel:number = 0;

  setLogLevel(value:number){
    this.logLevel = value;
  }
  setLogFile(path:string){
    this.logFile = path;
  }

  constructor(prefix: string) {
    const currentDate = new Date();
    const year = currentDate.getFullYear();
    const month = ("0" + (currentDate.getMonth() + 1)).slice(-2);
    const day = ("0" + currentDate.getDate()).slice(-2);

    // 组合完整的日期时间字符串
    let logFileName:string = `${year}${month}${day}`;

    this.prefix = prefix;
    this.domain = 0x0001;
    this.logLevel = LOG_ERROR;
    let context = getContext() as common.UIAbilityContext;
    this.logFile = context.filesDir + '/sdp_log.txt';
    console.log('logfile init is '+this.logFile);
    const files = fs.listFileSync(context.filesDir);
    console.log('logfile init list :'+files)
  }
  // 写入日志到文件
  private writeToFile(msg: string): void {
    try {
      console.log('写入文件内容:here 1');
      const file:fs.File = fs.openSync(this.logFile,
        fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE
      );
      console.log('写入文件内容:here 2');
      const timestamp = new Date().toString();
      const logLine = `${timestamp} [${LOG_TAG}] ${msg}\n`;
      // const encoder = new util.TextEncoder(); // 创建UTF-8编码器
      // const uint8Array = encoder.encode(logLine); // 字符串 → Uint8Array
      // 1. 获取文件状态(含大小)
      console.log('写入文件内容:here 3');
      const stats = fs.statSync(this.logFile);
      console.log('写入文件内容:here 4');
      const offset = stats.size; // 文件当前字节长度
      // 2. 以追加模式打开文件

      fs.writeSync(file.fd, logLine, { offset });
      console.log('写入文件内容:here 5');
      console.log('写入文件内容:'+logLine);
      //fs.writeSync(file.fd, uint8Array.buffer); // 写入文件
      fs.closeSync(file);
      console.log('写入文件内容:here 6');
    } catch (err) {
      hilog.error(0x0000, LOG_TAG, `${this.logFile},写日志失败: ${JSON.stringify(err)}`);
    }
  }
  debug(...args: string[]) {
    hilog.debug(this.domain, this.prefix, this.format, args);
    this.writeToFile(`INFO: ${args}`);
  }

  info(...args: string[]) {
    hilog.info(this.domain, this.prefix, this.format, args);
    this.writeToFile(`INFO: ${args}`);
  }

  warn(...args: string[]) {
    hilog.warn(this.domain, this.prefix, this.format, args);
    this.writeToFile(`INFO: ${args}`);
  }

  error(...args: string[]) {
    hilog.error(this.domain, this.prefix, this.format, args);
    this.writeToFile(`INFO: ${args}`);
  }

  fatal(...args: string[]) {
    hilog.fatal(this.domain, this.prefix, this.format, args);
    this.writeToFile(`INFO: ${args}`);
  }

  log(level:number,...args: string[]) {
    let TAG = '';
    if(level>=this.logLevel)
    {
      switch (level){
        case LOG_DEBUG:
          TAG = 'DEBUG';
          hilog.debug(this.domain, this.prefix, this.format, args);
          break;
        case LOG_INFO:
          TAG = 'INFO';
          hilog.info(this.domain, this.prefix, this.format, args);
          break;
        case LOG_WARN:
          TAG = 'WARN';
          hilog.warn(this.domain, this.prefix, this.format, args);
          break;
        case LOG_ERROR:
          TAG = 'ERROR';
          hilog.error(this.domain, this.prefix, this.format, args);
          break;
      }
      this.writeToFile(`${TAG}: ${args}`);
    }
  }

  isLoggable(level: number) {
    hilog.isLoggable(this.domain, this.prefix, level);
  }
}
export const sdp_logger = new SdpLogger('[sdp_client]');

worker的接收消息处引用sdp_logger这个单例并报错

cke_11268.png


更多关于HarmonyOS鸿蒙Next中在woker中调用单例对象,到调用对象函数的地方就报not initialized的实战教程也可以访问 https://www.itying.com/category-93-b0.html

2 回复

在HarmonyOS Next中,Worker线程调用单例对象报"not initialized"错误,是因为单例未在主线程初始化。鸿蒙的单例对象需要在主线程先完成初始化才能在Worker中使用。解决方法是在应用启动时(如Ability的onCreate)先调用该单例的getInstance()方法进行初始化。Worker线程中调用时需确保单例已初始化完成。注意单例的线程安全问题,必要时加同步锁。

更多关于HarmonyOS鸿蒙Next中在woker中调用单例对象,到调用对象函数的地方就报not initialized的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


这个问题是因为Worker线程和主线程的内存空间是隔离的,在Worker中无法直接访问主线程的单例对象。在HarmonyOS中,Worker运行在独立的线程环境中,与UI主线程不共享内存空间。

解决方案:

  1. 在Worker线程中重新初始化Logger实例,而不是直接使用主线程导出的单例。

  2. 或者通过Worker的postMessage机制,将日志信息发送回主线程,由主线程的Logger实例处理。

  3. 修改Logger类,使其支持在Worker环境中初始化:

// 在Worker中这样初始化
const logger = new SdpLogger('[worker]');
logger.setLogFile(getLogFile()); // 需要实现Worker环境下的文件路径获取

根本原因是Worker线程无法直接访问主线程的UIAbilityContext等资源,需要独立初始化。建议采用第一种方案,在Worker线程中创建独立的Logger实例。

注意:Worker中的文件操作路径需要适配Worker环境,不能直接使用UIAbilityContext的文件路径。

回到顶部