远程图片资源管理的最佳实践

示例

加载远程 jpg 图片,并渲染到 Sprite 时,我们可以考虑采用如下原始没有封装过的代码去实现:

import {
  _decorator,
  assetManager,
  Component,
  ImageAsset,
  isValid,
  Sprite,
  SpriteFrame,
  Texture2D,
} from "cc";

const { ccclass, property } = _decorator;

@ccclass
export default class TestComponent extends Component {
  @property(Sprite)
  sprite: Sprite = null!;

  protected onEnable(): void {
    const url = "https://xxx.jpg";
    assetManager.loadRemote<ImageAsset>(
      url,
      (error: Error | null, imageAsset: ImageAsset) => {
        if (error) {
          return;
        }
        if (!isValid(this.node)) {
          return;
        }

        // 创建纹理
        const texture = new Texture2D();
        texture.image = imageAsset;

        // 创建精灵
        const spriteFrame = new SpriteFrame();
        spriteFrame.texture = texture;

        // 渲染
        this.sprite.spriteFrame = spriteFrame;
      }
    );
  }

  protected onDestroy(): void {
    // TODO
  }
}

如何释放?

在这个组件销毁时,我们希望能完全释放这个过程中产生的所有资源,那么你认为的最佳代码是?请留下你的最佳实践~

在回答之前,大家可能需要注意的点:

  1. 上述代码中,存在三个 Asset 对象,分别是 imageAssettexturespriteFrame ,你认为他们都需要管理并释放吗,或者只有部分需要管理并释放?

  2. 你是如何释放的呢?直接 destroy()? 通过 addRef()decRef() ? 或者其他办法呢?

  3. 如果你通过 addRef()decRef() 管理,那么你还会主动调用 texture.destroy() 以销毁纹理吗

  4. 你是否考虑过,texture 可能会在运行时被 动态合图 了?如果你没有注意到这一点,现在可以注意啦,请分别列出 texture 被合图和没有被合图情况下的资源释放处理。

  5. 当你编写完代码之后,你是如何验证真的都被释放了?

4赞

题出的不错,我之前是加载什么就释放什么,动态合图在释放资源的同时调用 cc.dynamicAtlasManager.deleteAtlasTexturecc.dynamicAtlasManager.deleteAtlasSpriteFrame,只不过我还没验证远程资源,我后面试试

另外,释放资源最好使用 assetManager.releaseAsset,不要用等待 refCount 为 0 时的自动释放,否则会出现 加载当前资源导致加载返回资源是已释放后的,我在自己框架里面是用 releaseAsset 做的

第四条,被动态合图之后,原本的texture、imageAsset就没用了,甚至可以直接释放;

this.scheduleOnce(()=>{
    assetManager.releaseAsset(texture)
    assetManager.releaseAsset(imageAsset)
})

有用的,动态合图后 spriteFrame 对象会保留原本的资源引用,我之前做的精灵按钮组件就会使用原本的资源,应该是 spriteFrame.original._texture

最佳实践在这:Creator3.x引擎的动态资源加载和释放方案

我测试了下,我之前的方案是有效的,加载远程图片时,对依赖其创建的 SpriteFrame 和 Texture2D 执行 addRef 、decRef、assetManager.releaseAsset 都不会影响远程资源 Image 对象的引用计数和有效性,所以只需要管理加载的资源

唯一需要注意的就是释放远程 Image 时,动态合图的图集中的 Texture 需要手动释放或者写另外的逻辑

当你发现这个东西很难用, 很难理解时,大概率是这个框架比较烂

HTMLImageElement创建的spf…应该怎么释放…?
我直接assetManager.releaseAsset或者destroy…
动态合图那边就会不断报错…
现在要设置spf.packable=false…才能删除…

试了你的dynamicAtlasManager.deleteAtlasTexture…
好像也不太行…