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);
如果你要动态加载图集里的图,那么就不要给他拉任何图片。
今天的内容就到这里,希望对你有帮助。