鸿蒙|性能优化-内存及其他优化

概览:本文讲解了内存优化的核心知识,内存泄漏、LRUCache缓存、资源加载与释放、内存动态管理,并拓展了其他常见性能优化点。

什么是内存优化?

内存优化:降低应用运行时内存占用,避免内存泄漏、溢出等问题。

内存:RAM(Random Access Memory)随机存取存储器,通常被称为运行内存或主存。

为什么要进行内存优化?

内存占用过高会导致应用卡顿(丢帧)、后台存活率低、设备发烫、耗电增加、启动速度慢等表现,也会间接加剧CPU的负载,如果内存溢出,还会造成应用立即崩溃。

内存优化的方向

1、ArkTS内存优化

typescript 复制代码
1. 避免内存泄漏
ArkTs有一个垃圾回收机制,认为你不用的变量会帮你回收掉。
比如getData()里,如果只写了let str = ''一行代码,在执行完成后str里面存的内容最后就被回收掉了。
但是现在我又return了一个函数,这个函数访问了外面的str,最终这个函数又被fn()进行一个接收,这就会产生了一个内存泄漏。
内存泄露原因:str始终处于一个可访达的状态,这个变量就不会被垃圾回收。

import { promptAction } from '@kit.ArkUI'

function getData() {
  let str = ''
  return () => {
    promptAction.openToast({
      message: `str的内容为:${str}`
    })
    str += 'a'
  }
}

const fn = getData()

@Entry
@Component
struct Index {
  build() {
    Column() {
      Button('闭包')
        .onClick(() => {
          fn()
        })
    }
  }
}

2. 使用LRUCache缓存
LRUCache类似AppStorage和LocalStorage,但是不具备数据产生变化UI更新的能力,就是一个全局缓存的工具。
优点:AppStorage和LocalStorage可以一直存内容,但是LRUCache有存储上线(默认64),根据最少使用的规则去覆盖之前存过的东西,更轻量级一些。

import { util } from '@kit.ArkTS'

const lruCache: util.LRUCache<string, Object> = new util.LRUCache(64) // 初始化(默认64)
lruCache.put('key', 0) // 添加
lruCache.get('key') // 获取
lruCache.remove('key') // 删除
lruCache.clear() // 清空

3. 使用合理尺寸的图片
8k分辨率的图片:占用内存127MB
4k分辨率的图片:占用内存32MB
2k分辨率的图片:占用内存14MB

2、 资源内存优化

arduino 复制代码
1.预置图片加载优化
预制图片 > 纹理转换 > 超压缩编码 > 超压缩解码 > 渲染 > 显示
在build-profile.json5里开启

"resOptions": {
  "copyCodeResource": {
    "enable": false
  },
  "compression": {
    "media": {"enable": true}, // enable 是否启用纹理压缩
    "filters": [
      {
        "method": {"type": "sut","blocks": "4x4"}, // 转换类型
        "files": {"path":["./**/*"],"size": [[0,'1000k']]}, // 匹配条件:哪些目录或者哪些尺寸的图片可以开启超压缩
        "exclude": {"path": ["./**/*.webp"],"size": [[0,"10k"]]} // 移除匹配
      }
    ]
  }
}

文件                        耗时          内存占用
原图(.png)                 62.103ms     598965KB
纹理超压缩(.sut)            15.309ms     167723KB
自适应可变纹理压缩(.astc)    38.239ms     167723KB

2.资源释放优化
分帧渲染完成后释放:this.displayAbi?.stop() this.displayAbi?.off('frame')
文件读写完成后释放:fileIo.close()
录音完成后释放:stop、release、finish、shutdown
使用完后要停止引擎工作,释放引擎资源。

案例:内存动态管理

1、内存动态管理

scss 复制代码
EntryAbility.ets
// 内存发生变化时会触发的回调函数,level内存级别
onMemoryLevel(level: AbilityConstant.MemoryLevel): void {
  // 判断设备的内存情况,是否进行自己的内存释放
  if (level === AbilityConstant.MemoryLevel.MEMORY_LEVEL_MODERATE) {
    // 不重要的资源进行内存释放
  } else if (level === AbilityConstant.MemoryLevel.MEMORY_LEVEL_LOW) {
    // 停止一些非核心任务
  } else if (level === AbilityConstant.MemoryLevel.MEMORY_LEVEL_CRITICAL) {
    // 释放所有不影响程序运行的内存
  }
}

2、缓存工具 ets > utils > MemoryManager

typescript 复制代码
import { util } from '@kit.ArkTS'

class MemoryManger<T> {
  lruCache: util.LRUCache<string, T>

  constructor(length = 50) {
    this.lruCache = new util.LRUCache(length)
  }

  put(key: string, value: T) {
    this.lruCache.put(key, value)
  }

  get(key: string) {
    return this.lruCache.get(key)
  }

  clear() {
    this.lruCache.clear()
  }
}

// 画布数据
export const canvasMemory = new MemoryManger<ImageData>()

3、画布

Canvas画布:developer.huawei.com/consumer/cn...

CanvasRenderingContext2D:developer.huawei.com/consumer/cn...

kotlin 复制代码
import { canvasMemory } from '../utils/MemoryManager'

@Entry
@Component
struct Index {
  ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D()
  @State canvasWidth: number = 0
  @State canvasHeight: number = 0
  @Watch('setColor') @State colorVal: number = 0
  @Watch('setWidth') @State widthVal: number = 6
  @State step: number = 0 // 画布展示的是哪一步
  @State maxStep: number = 0 // 绘制的总步数

  build() {
    Column() {
      this.CanvasBuilder() // 画布
      Column({ space: 12 }) {
        this.canvasCacheBuilder() // 画布缓存
        this.canvasModificationBuilder() // 画布修改
      }.width('100%')
      .padding(12)
    }.width('100%')
    .height('100%')
    .backgroundColor('#f5f7f8')
  }

  // 画布
  @Builder
  CanvasBuilder() {
    Canvas(this.ctx)
      .width('100%')
      .layoutWeight(1)
      .backgroundColor('#fff')
      .onReady(() => {
        this.ctx.lineWidth = this.widthVal
        this.ctx.strokeStyle = 'rgb(0,0,0)'
      })
      .onAreaChange((_, _new) => {
        this.canvasWidth = _new.width as number
        this.canvasHeight = _new.height as number
      })
      .gesture(PanGesture()
        .onActionStart((e) => {
          this.ctx.beginPath() // 开始
          this.ctx.moveTo(e.fingerList[0].localX, e.fingerList[0].localY)
        })
        .onActionUpdate((e) => {
          this.ctx.lineTo(e.fingerList[0].localX, e.fingerList[0].localY)
          this.ctx.stroke()
        })
        .onActionEnd(() => {
          this.ctx.closePath() // 结束
          this.step++ // 画完一步之后+1
          this.maxStep++
          const canvasData = this.ctx.getImageData(0, 0, this.canvasWidth, this.canvasHeight)
          canvasMemory.put(this.step.toString(), canvasData) // 存储画布数据
        })
        .onActionCancel(() => {
          this.ctx.closePath() // 结束
          this.step++
          this.maxStep++
          const canvasData = this.ctx.getImageData(0, 0, this.canvasWidth, this.canvasHeight)
          canvasMemory.put(this.step.toString(), canvasData) // 获取画布数据
        }))
  }

  // 画布缓存
  @Builder
  canvasCacheBuilder() {
    Row() {
      Button('上一步')
        .fontSize(24)
        .enabled(this.step > 0)
        .onClick(() => {
          this.step--
          const canvasData = canvasMemory.get(this.step.toString())
          if (!canvasData) {
            this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
          } else {
            this.ctx.putImageData(canvasData, 0, 0)
          }
        })

      Button('下一步')
        .fontSize(24)
        .enabled(this.step < this.maxStep)
        .onClick(() => {
          //   TODO
          this.step++
          const canvasData = canvasMemory.get(this.step.toString())
          this.ctx.putImageData(canvasData, 0, 0)
        })

      Button('清空画布')
        .fontSize(24)
        .onClick(() => {
          this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
        })
    }.width('100%')
    .justifyContent(FlexAlign.SpaceAround)
  }

  // 画布修改
  @Builder
  canvasModificationBuilder() {
    Column({ space: 12 }) {
      Row({ space: 12 }) {
        Text('画笔颜色')
        Slider({
          value: $$this.colorVal,
          min: 0,
          max: 255 * 7,
        })
          .blockColor(this.setColor())
          .selectedColor(Color.Transparent)
          .trackColor(new LinearGradient([{ color: 'rgb(0,0,0)', offset: 0 },
            { color: 'rgb(0,0,255)', offset: 1 / 7 }, { color: "rgb(0,255,0)", offset: 2 / 7 },
            { color: "rgb(0,255,255)", offset: 3 / 7 }, { color: "rgb(255,0,0)", offset: 4 / 7 },
            { color: "rgb(255, 0, 255)", offset: 5 / 7 }, { color: "rgb(255, 255, 0)", offset: 6 / 7 },
            { color: "rgb(255, 255, 255)", offset: 1 }]))
          .layoutWeight(1)
      }
      .width('100%')

      Row({ space: 12 }) {
        Text('画笔粗细')
        Slider({
          value: $$this.widthVal,
          min: 2,
          max: 10,
          style: SliderStyle.InSet
        })
          .selectedColor(Color.Transparent)
          .trackColor(Color.Black)
          .blockSize({
            width: this.widthVal,
            height: this.widthVal
          })
          .layoutWeight(1)
      }
      .width('100%')
    }
    .justifyContent(FlexAlign.SpaceAround)
  }

  setColor() {
    const color = Math.floor(this.colorVal / 255)
      .toString(2)
      .padStart(3, '0')
      .split('')
      .reverse()
      .join(',')
      .replace('0', (this.colorVal % 255).toString())
      .split(',')
      .reverse()
      .map((item: string) => {
        return item === '1' ? '255' : item
      })
      .join(',')
    this.ctx.strokeStyle = `rgb(${color})`
    return `rgb(${color})`
  }

  setWidth() {
    this.ctx.lineWidth = this.widthVal
  }
}

其他优化

arduino 复制代码
1.尽量使用const声明常量
2.减少使用嵌套 export*/import* 的方式
3.减少全局变量存储,使用局部变量提升属性访问速度
4.防止传感器滥用
5.减少不必要的后台任务,防止抢占资源
6.防止算法时间复杂度失控
7.清理冗余文件和代码
8.使用HSP代理HAR(HAR包跟随打包)
9.删除无用依赖
相关推荐
总有刁民想爱朕ha2 小时前
haihong Os 鸿蒙开源版开发一个pc版软件应用(1)
华为·开源·harmonyos
弓.长.6 小时前
ReactNative for OpenHarmony项目鸿蒙化三方库:react-native-masked-view — 遮罩视图组件
react native·react.js·harmonyos
大雷神6 小时前
HarmonyOS APP<玩转React>开源教程十四:进度管理服务
前端·react.js·开源·harmonyos
Swift社区6 小时前
鸿蒙 App 的数据流设计
华为·harmonyos
国医中兴6 小时前
Flutter 三方库 superclass 的鸿蒙化适配指南 - 支持原生高性能类构造、属性代理与深层元数据解析实战
flutter·harmonyos·鸿蒙·openharmony
Swift社区6 小时前
Flutter 迁移鸿蒙 ArkUI 的真实成本
flutter·华为·harmonyos
互联网散修7 小时前
零基础鸿蒙应用开发第七节:流程控制之分支语句
harmonyos·流程控制·鸿蒙零基础入门
casual_clover7 小时前
关于 HarmonyOS 版本的简述
harmonyos·版本信息
GitCode官方7 小时前
AtomGit 携手开源鸿蒙,推动智能终端操作系统生态繁荣
华为·开源·harmonyos·atomgit