前言
在之前的那片文章上写了我们可以根据Asset的依赖,来做ab包的加载。这样可以在Asset不在用的时候及时释放内存中的资源,减少内存的峰值。也可以减少一次性回收资源太多,导致cpu峰值。
正文
设计思路
准备阶段
首先我们需要在打资源的时候做一次资源相互依赖的配置。然后把这份配置放置在StreamingAssets下,然后可以动态热更下来。在使用的时候,我们先根据ab包里总的Minifest来把每个ab包中有那些资源给读取出来,然后再根据资源依赖关系配置,我们就可以把这两个文件关联起来,asset->depedencyAssets->assetbundle。
加载阶段
根据这样的关系我们在加载的时候,当需要加载一个asset的时候,我们可以去读取它所关联的depedencyAssets资源,然后判断这个资源有在被使用,如果被使用了,我们就给他的引用计数+1;如果没有我们就根据这个asset去找它在哪个assetbundle中,然后把这个assetbundle给加载出来,再看下它所引用的asset有没有加载出来,加载出来就引用计数加1。在这里我们可以看到,只在第一次加载的时候会去给它所依赖的Asset做一次引用计数+1,因为依赖的Asset只需要去关心它是不是被依赖了,而不需要关心依赖它的那个资源被加载几次;因为只要它还在被引用,那就自然而然依赖的资源就不能被回收释放的。
卸载阶段
当我们需要卸载一块资源的时候,我们首先要做的就是,把这个资源的引用计数给-1,然后当他已经完全没有被引用了,那我们就去寻找它所依赖的资源,然后也都减1;然后我们查找这个资源所在的assetbundle包,去遍历它里面的Asset是不是都不被引用了,如果都不被引用了,我们就给这个资源做个标记要被回收的;不立刻被回收而是去做标记的好处就是,当我们的下一次还要用ab包里的资源的时候,我们可以不用再去加载ab包了,直接使用从这个ab包里拿资源,然后把这个ab包在状态给修改回去。我们将找一个空余的时间去把所有不用的ab包给回收掉。
关注点
这张图我们看到没有我们要加载的资源
这张图是我们第一次加载资源的时候它所被引用到的对象
这个时候当我们调用Resources.UnloadAsset卸载资源,它就会变成第一张图,这个资源就从内存里消失了,然后我们不去卸载ab包,第二次我们直接调用资源加载,我们看到的情况和之前是一样的。
但是,如果我们这个时候把ab包给卸载了,然后重新加载ab再加载asset之后,我们将看到这样的资源依赖图
这个时候我们再去调用Resources.UnloadAsset卸载资源你会看到资源没有被依赖,但是没有从内存中消失。
这个时候我们要卸载这个图片必须得调用assetbundle.Unload(true)来卸载,或者调用Resources.UnloadUnusedAssets()这来卸载,后一种卸载方式是会循环遍历所有的资源然后去卸载,所有会有很大的消耗;前一种的卸载方式是把资源依赖给强行解放了,所以当你实际上资源还在使用的话,就会变紫色,丢资源。
在这种Asset没有被回收的情况下,我们assetbundle没有回收,然后我们再从assetbundle中把资源加载出来,实际上使用的资源还是这个,它会被重新被引用上。但是如果我们调用了assetbundle.Unload(false)来把ab给回收了,但是没有去调用Resources.UnloadUnusedAssets()来回收asset,那你会发现这个丢失引用的资源就成了冗余资源,我再次从新加载ab,加载asset的时候,就是重新生成一份资源了
就成这样的情况了。
总结
源码地址
我们在做asset的资源管理的时候,一般来说直接调用true来做一次强行的资源回收,但是如果需要很在意表现,不想表现穿帮,对于一些资源就算冗余也无所谓的情况下,那最后调用一次Resources.UnloadUnusedAssets()来做一次资源的回收兜底行为。下一次我是做场景加载呢,还是做一个资源打包编辑器呢?看时间吧!鸽的前兆。