UnityWebRequest加载显示图片的内存泄漏总结

UnityWebRequest加载显示图片的内存泄漏总结

我们经常在使用UnityWebRequest发现内存无法释放问题,总是一知半解的,希望这个测试能对你在UnityWebRequest加载图片方面有更清晰的认识。

首先我们来个简单的例子,首先上代码,大家看下这个代码有什么问题

第一个例子

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
public class Cload0 : MonoBehaviour
{
    public Button btnclick;
    public RawImage rawimg;
    
    // Start is called before the first frame update
    void Start()
    {
        btnclick.onClick.AddListener(OnClickLoad);
    }
    
    void OnClickLoad()
    {
        StartCoroutine(load());
    }
    IEnumerator load()
    {
        UnityWebRequest www;
        DownloadHandlerTexture downloadTexture;
        string localurl = Application.streamingAssetsPath;

        string filename, url;
        filename = "1.png";
        url = string.Format("{0}/{1}", localurl, filename);

        www = UnityWebRequest.Get(url);

        downloadTexture = new DownloadHandlerTexture(true);
        www.downloadHandler = downloadTexture;
        yield return www.SendWebRequest();
#if UNITY_EDITOR
        Debug.Log("<color=#6780AB>LoadRoot-本地下载:</color>" + url);
#endif
        www.Dispose();
    }

}

上面代码是在UI上放一个按钮,点击后执行OnClickLoad函数,开始加载本地图片,我们第一个测试是只进行Dispose操作,是否可以释放掉内存,是不是觉得这样应该能释放掉,来用都没用也没引用,应该没有内存泄漏。
经过多次点击按钮,大家看看内存情况。
在这里插入图片描述
在NotSaved里的Texture2D里,这几个就是点击了几次生成的,看来是无法释放掉了。
有人说调用GC释放内存。我加了
Resources.UnloadUnusedAssets();
System.GC.Collect();
也都是无济于事的。

下面我们进行第二次测试。我们增加一行代码

测试后的改动

		yield return www.SendWebRequest();
#if UNITY_EDITOR
        Debug.Log("<color=#6780AB>LoadRoot-本地下载:</color>" + url);
#endif
        DestroyImmediate(DownloadHandlerTexture.GetContent(www));
        //DestroyImmediate(((DownloadHandlerTexture)www.downloadHandler).texture);
        www.Dispose();

我们增加了DestroyImmediate函数,直接把www的Texture2D释放掉,经过我的测试,内存没有增长,下屏蔽的那行www.downloadHandler.texture也可以释放掉。

下面我们开始进行实际的图片显示来测试。

加上图片显示,进一步测试

第三个测试,我们上代码

		yield return www.SendWebRequest();
#if UNITY_EDITOR
        Debug.Log("<color=#6780AB>LoadRoot-本地下载:</color>" + url);
#endif

        rawimg.texture = DownloadHandlerTexture.GetContent(www);
        www.Dispose();

我们增加RawImage组件rawimg,并给他显示。我们点击了几次后,内存继续变大了,那么如果我们要释放内存要怎么做了。经过我几次测试后,发现最好的写法是如下:

		yield return www.SendWebRequest();
#if UNITY_EDITOR
        Debug.Log("<color=#6780AB>LoadRoot-本地下载:</color>" + url);
#endif
        if (rawimg.texture)
        {
            DestroyImmediate(rawimg.texture);
        }
        rawimg.texture = DownloadHandlerTexture.GetContent(www);
        www.Dispose();

首先判断rawimg的纹理存在,把他销毁掉,然后再进行获取新的纹理。这样在点击多次后,内存只有显示在raw里的一份。我们可以在UI关闭或者销毁的时候释放掉这份Texture就可以了。

注意点
这里有一个需要注意的地方,希望大家最好不要这样写。

        Texture2D t = DownloadHandlerTexture.GetContent(www); 
        if (rawimg.texture)
        {
            DestroyImmediate(rawimg.texture);
        }
        rawimg.texture = t;

如果你自己管理UnityWebRequest,或者利用下载一次,多次使用的情况,可能你会把www保存下来。
如果你的rawimg里已经显示了www(1.png)的内容,那么你下次再次给rawimg给赋值1.png的时候,你获取了t后,销毁了rawimg里的1.png,那么你的t里也被销毁了,会导致你的rawimg.texture是null。
这里再说明下DownloadHandlerTexture.GetContent函数,他如果发现为空,那么他会重新生成一个Texture2D。

Image的写法
另外对于Image组件可以这样写。

		yield return www.SendWebRequest();
#if UNITY_EDITOR
        Debug.Log("<color=#6780AB>LoadRoot-本地下载:</color>" + url);
#endif
        if (rawimg.sprite)
        {
            if (rawimg.sprite.texture)
                DestroyImmediate(rawimg.sprite.texture);
            Destroy(rawimg.sprite);
        }
        Texture2D texture2d = DownloadHandlerTexture.GetContent(www);
        Sprite temp = Sprite.Create(texture2d, new Rect(0, 0, texture2d.width, texture2d.height), new Vector2(0, 0));
        rawimg.sprite = temp;
        
        www.Dispose();

基本到这里加载PNG的释放问题到这里就解决了。

关于图集Sprite的释放

但是还有一种是加载图集中的图片,如果不注意也会有内存泄漏问题。

举例说明,我们如果要通过代码去显示一个图集里图片一般使用

imgAssetBilinear.GetSprite(sName); //imgAssetBilinear是SpriteAtlas

得到一个Sprite,这个Sprite是new出来的,如果不释放也会存在内存中。我们只需要

if (qualityImage.sprite)
	UnityEngine.U2D.SpriteAtlas.Destroy(qualityImage.sprite);
qualityImage.sprite = imgAssetBilinear.GetSprite("marker_quality_icon_");

这里需要注意的是,如果你的qualityImage.sprite 在预制体中已经有图集里的元素了就是出现一个报错提示:
Destroying assets is not permitted to avoid data loss.
If you really want to remove an asset use DestroyImmediate (theObject, true);
如果你要动态加载图集里的图,那么就不要给他拉任何图片。

今天的内容就到这里,希望对你有帮助。

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值