HarmonyOS 鸿蒙Next Taskpool字符串宽度计算与性能影响 HarmonyOS 鸿蒙Next TaskGroup大量执行时是否影响UI主线程性能 HarmonyOS 鸿蒙Next 模拟器执行execute并操作UI卡顿崩溃问题

发布于 1周前 作者 caililin 来自 鸿蒙OS

HarmonyOS 鸿蒙Next Taskpool字符串宽度计算与性能影响
HarmonyOS 鸿蒙Next TaskGroup大量执行时是否影响UI主线程性能
HarmonyOS 鸿蒙Next 模拟器执行execute并操作UI卡顿崩溃问题

小说章节数据加载时会进入HandleTaskPool这个函数,按批次每批batchSize:100个章节的字符串数据进入batchTaskPool函数进行多线程taskpool处理,不清楚重复改变taskGroup1会不会有影响。最终数据处理成this.AllChaptersContent

taskpool.execute处理数据的时候进行UI操作,比如改变当前章节,模拟器会卡顿,将batchSize缩小改成10也会有崩溃的情况

//Canvas多线程方案,按字拆分,taskpool多线程处理
import { chaptersItem } from 'ets/componets/dataList/ReaderChaptersItem';
import { taskpool } from '@kit.ArkTS';

@Component
export struct TxtContentHandler {
  @Prop @Watch('HandleTaskPool') txtFile: chaptersItem[] = [];
  @Prop @Watch('CurrentChaptersChange') CurrentChapters: number = 0;
  @Prop @Watch('CurrentFontSizeChange') CurrentFontSize: number = 0;
  @Prop LineHeight: number = 1.8; // 百分比
  @Prop ParagraphSpacing: number = 0;
  @Prop ReaderPadding: number = 0;
  @State AllChaptersContent: Array<string>[] = []; // 存放需要显示在页面上每个章节重排段落的字符串,一层数组索引为章节,二层数组索引为段落
  @State tempAllChaptersContent: Array<string>[] = [];
  @State lines: string[] = [];
  @State currentPage: number = 0;
  @State ShowHeight: number = 0
  @State @Watch('HandleTaskPool') ShowWidth: number = 0
  @State linesPerPage: number = 0;
  @State isChangeTimer: number = 0;
  @State isTaskPoolHandleNumber: number = 0;
  @State SleepTime: number = 0;
  @State innerSleepTime: number = 0;
  @State timer: number = 0;
  @State taskGroup1: taskpool.TaskGroup = new taskpool.TaskGroup();
  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);

  // 获取总页数
  GetTotalPages() {
    return Math.floor(this.AllChaptersContent[this.CurrentChapters]?.length / this.linesPerPage);
  }

  async batchTaskPool(batchStart: number, batchSize: number) {
    this.taskGroup1 = new taskpool.TaskGroup();
    for (let i = batchStart; i < Math.min(batchStart + batchSize, this.txtFile.length); i++) {
      let dataArray: Array<string> = [
        this.txtFile[i].content,
        this.CurrentFontSize.toString(),
        this.ShowWidth.toString(),
      ]

      let task: taskpool.Task = new taskpool.Task(HandleAllChaptersContentTaskPool, dataArray);
      this.taskGroup1.addTask(task);
    }
    return taskpool.execute(this.taskGroup1, taskpool.Priority.LOW).then(res => {
      for (let i = 0; i < res.length; i++) {
        this.AllChaptersContent[batchStart+i] = res[i] as Array<string>;
      }
    }).catch(error => {
      console.error(`taskpool excute error: ${error}`);
    })
  }

  Sleep(ms: number, str: string): Promise<void> {
    return new Promise(resolve => {
      this.SleepTime = setTimeout(resolve, ms)
    })
  }

  // 多线程处理
  async HandleTaskPool() {
    this.isTaskPoolHandleNumber++ // 多次调节字体大小或者其它时,防止重复渲染
    let NowTaskPoolHandleNumber = this.isTaskPoolHandleNumber // 同上,只有最新isTaskPoolHandleNumber才可以进入下面判定
    await this.Sleep(2000, 'out') // 控制任务执行间隔,这个数字尽量调大一点,如果调太小,会导致两个taskpool同时处理
    if (this.txtFile.length > 0 && this.ShowWidth > 0 && NowTaskPoolHandleNumber == this.isTaskPoolHandleNumber) {
      // 需要txt文件加载和showWidth同时准备好,且是最新的isTaskPoolHandleNumber,才进入处理数据并渲染,
      console.log('TaskPoolHandleNumber:' + NowTaskPoolHandleNumber)
      let NowChapters = this.CurrentChapters
      const batchSize = 100; // 每批次处理的任务数量,爆内存就调小

      for (let batchStart = NowChapters; batchStart < this.txtFile.length; batchStart += batchSize) {
        // 优先处理当前章节数据,并渲染到页面上
        if (NowTaskPoolHandleNumber == this.isTaskPoolHandleNumber) {
          let taskpoolInfo: taskpool.TaskPoolInfo = taskpool.getTaskPoolInfo();
          console.log('threadInfos:' + JSON.stringify(taskpoolInfo.threadInfos))
          this.drawPage() // 页面渲染函数
          await this.batchTaskPool(batchStart, batchSize)
          console.log('batchStartTaskpool done:' + Math.min(batchStart + batchSize, this.txtFile.length));
        } else {
          // 只处理最新的文字,旧的直接退出,防止重复处理
          break
        }
      }

      if (NowChapters > 0) {
        // 如果当前章节不为0,前面的章节要补上
        for (let batchStart = 0; batchStart < NowChapters; batchStart += batchSize) {
          if (NowTaskPoolHandleNumber == this.isTaskPoolHandleNumber) {
            await this.batchTaskPool(batchStart, batchSize)
            console.log('batchStartTaskpool done:' + Math.min(batchStart + batchSize, this.txtFile.length));
          } else {
            // 只处理最新的文字,旧的直接退出,防止重复处理
            break
          }
        }
      }

      if (NowTaskPoolHandleNumber == this.isTaskPoolHandleNumber) {
        if (this.tempAllChaptersContent.length > 0) {
          this.AllChaptersContent = this.tempAllChaptersContent
        }
        this.drawPage();
        console.log('taskpool handle finish')
        this.isTaskPoolHandleNumber = 0
      } else {
        console.log(`${NowTaskPoolHandleNumber} AllBreak`)
      }
    }
  }

  async CurrentChaptersChange() {
    this.currentPage = 0
    clearTimeout(this.timer)
    this.timer = setTimeout(() => {
      this.drawPage();
      console.log('CurrentChapters:' + this.CurrentChapters)
      console.log('currentPage:' + this.currentPage)
      console.log('totalPages:' + this.GetTotalPages())
    }, 100)
  }

  CurrentFontSizeChange() {
    clearTimeout(this.timer)
    this.timer = setTimeout(() => {
      console.log('CurrentFontSize:' + this.CurrentFontSize)
      this.drawPage();
      this.HandleTaskPool()
    }, 100)
  }

  drawPage() {
    // 页面渲染函数,渲染到canvas上
    this.context.font = `${vp2px(this.CurrentFontSize)}px`;
    this.linesPerPage = Math.floor((this.ShowHeight) / (this.CurrentFontSize * this.LineHeight));
    if (this.context && this.AllChaptersContent[this.CurrentChapters]) {
      this.context.font = `${vp2px(this.CurrentFontSize)}px`;
      this.context.fillStyle = 'black';
      const start = this.currentPage * this.linesPerPage;
      const end = start + this.linesPerPage;
      const pageLines = this.AllChaptersContent[this.CurrentChapters].slice(start, end);
      this.context.clearRect(0, 0, this.ShowWidth, this.ShowHeight);
      pageLines.forEach((line, index) => {
        this.context.fillText(line, 0, (index + 1) * this.CurrentFontSize * Number(this.LineHeight));
      });
    }
  }

  nextPage() {
    if (this.currentPage < this.GetTotalPages()) {
      this.currentPage++;
      console.log('currentPage:' + this.currentPage + '\ntotalPages:' + this.GetTotalPages())
      this.drawPage();
    }
  }

  prevPage() {
    if (this.currentPage > 0) {
      this.currentPage--;
      console.log('prevPage:' + this.currentPage)
      this.drawPage();
    }
  }

  build() {
    Column() {
      Canvas(this.context)
        .layoutWeight(1)
        .onSizeChange((oldValue: SizeOptions, newValue: SizeOptions) => {
          this.ShowHeight = Number(newValue.height)
          this.ShowWidth = Number(newValue.width)
          this.drawPage()
          this.HandleTaskPool()
          console.log('currentPage:' + this.currentPage + '\ntotalPages:' + this.GetTotalPages())

          console.log('ShowHeight:' + this.ShowHeight)
          console.log('ShowWidth:' + this.ShowWidth)
          console.log('currentPage:' + this.currentPage + '\ntotalPages:' + this.GetTotalPages())
        })
      Row() {
        Button('上一页')
          .onClick(() => {
            this.prevPage()
          })
          .backgroundColor(this.currentPage > 0 ? 'blue' : 'gray')

        Button('下一页')
          .onClick(() => {
            this.nextPage()
          })
          .backgroundColor(this.currentPage < this.GetTotalPages() ? 'blue' : 'gray')
      }
      .borderWidth(1)
      .margin({ top: 10 })
    }
  }
}

@Concurrent
function HandleAllChaptersContentTaskPool(dataArray: Array<string>) {
  let content: string = dataArray[0]
  let CurrentFontSize = Number(dataArray[1])
  let ShowWidth = Number(dataArray[2])
  let chaptersItemLines: string[] = []
  let currentLine: string = ''
  let pattern = /\n/
  let i = 1
  while (i < content.length) {
    let temp = content[i] + content[i+1]
    if (!pattern.test(temp)) {
      if (Number(currentLine.length + 1) * CurrentFontSize < Number((ShowWidth - 10).toFixed(0))) {
        currentLine += content[i];
      } else {
        chaptersItemLines.push(currentLine);
        currentLine = content[i];
      }
      i++
    } else {
      chaptersItemLines.push(currentLine);
      i = i + 2
      currentLine = content[i];
    }
  }
  if (pattern.test(chaptersItemLines[chaptersItemLines.length-1])) {
    chaptersItemLines.splice(chaptersItemLines.length - 1, 1)
  }
  return chaptersItemLines
}

更多关于HarmonyOS 鸿蒙Next Taskpool字符串宽度计算与性能影响 HarmonyOS 鸿蒙Next TaskGroup大量执行时是否影响UI主线程性能 HarmonyOS 鸿蒙Next 模拟器执行execute并操作UI卡顿崩溃问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html

2 回复

使用真机试试

更多关于HarmonyOS 鸿蒙Next Taskpool字符串宽度计算与性能影响 HarmonyOS 鸿蒙Next TaskGroup大量执行时是否影响UI主线程性能 HarmonyOS 鸿蒙Next 模拟器执行execute并操作UI卡顿崩溃问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


HarmonyOS 鸿蒙Next Taskpool字符串宽度计算与性能影响

在HarmonyOS中,Taskpool用于管理并发任务。字符串宽度计算若频繁进行且任务繁重,可能会影响性能。优化建议包括减少不必要的计算、利用缓存机制存储常用结果,或采用异步处理方式来减轻主线程负担。

HarmonyOS 鸿蒙Next TaskGroup大量执行时是否影响UI主线程性能

TaskGroup用于管理一组并发或顺序执行的任务。大量执行时,若未合理管理任务优先级和调度,可能会抢占UI主线程资源,导致性能下降。建议合理划分任务,使用低优先级队列处理非UI相关任务,确保UI线程流畅运行。

HarmonyOS 鸿蒙Next 模拟器执行execute并操作UI卡顿崩溃问题

模拟器执行execute时操作UI出现卡顿或崩溃,可能由于多线程同步问题或资源竞争导致。确保UI操作在主线程进行,使用任务队列或信号量机制同步多线程访问UI资源。此外,检查代码中的内存泄漏和无效引用,避免资源耗尽导致崩溃。

如果问题依旧没法解决请联系官网客服, 官网地址是 https://www.itying.com/category-93-b0.html

回到顶部