在http://www.tuicool.com/articles/ZFrMZnM的基础上对AsyncImageDownload进行了完善,完整代码如下:
using UnityEngine;
using System.Collections;
using System.IO;
using System.Collections.Generic;
using System;
///
/// 图片(Texture)加载类
/// 三级缓存:1、如果在内存中,则先从内存获取;2、没有则查看本地是否存在,存在则从本地获取;3、没有则再从网络进行获取并保存到本地。
///
public class AsyncImageDownload : MonoBehaviour
{
///
/// 默认图片
///
public Texture placeholder;
///
/// 单例
///
public static AsyncImageDownload Instance = null;
///
/// 本地图片保存路径。如果是android环境,Application.persistentDataPath值为/storage/emulated/legacy/Android/data/com.xxx.xxx/files/
///
private string imageCacheRootPath = Application.persistentDataPath + "/ImageCache/";
///
/// 缓存在内存里的texture,重复利用。key是url对应图片保存成本地文件的绝对路径。
/// texture采用弱引用方式,当系统需要回收时这里不会因为强引用阻止回收。
///
private Dictionary textureCache = new Dictionary();
///
/// 应用开始运行时,可以调用此方法进行初始化
///
/// AsyncImageDownload单例对象
public static AsyncImageDownload CreateSingleton()
{
if (!Directory.Exists(Application.persistentDataPath + "/ImageCache/"))
{
Directory.CreateDirectory(Application.persistentDataPath + "/ImageCache/");
}
GameObject obj = new GameObject();
obj.AddComponent();
AsyncImageDownload loader = obj.GetComponent();
Instance = loader;
loader.placeholder = Resources.Load("default_cover") as Texture;// 默认图片
Debug.Log("image save path : " + Instance.imageCacheRootPath);
return loader;
}
///
/// 根据url加载对应图片。
///
/// 图片url
/// UITexture
/// 加载完成时的回调
/// 是否需要缓存到内存中(如果非长时间显示在页面,不建议缓存)
public void SetAsyncImage(string url, UITexture texture, Action action = null, bool needCache = true)
{
if (string.IsNullOrEmpty(url))
{
Debug.LogWarning("[SetAsyncImage] url is null.");
return;
}
// 只有需要缓存时才从内存中查找
if (needCache)
{
// 从内存获取
if (getFromCache(imageCacheRootPath + url.GetHashCode(), texture))
{
if (action != null)
{
action();
}
return;
}
}
//判断是否是第一次加载这张图片(以url的hashcode作为文件名)
if (!File.Exists(imageCacheRootPath + url.GetHashCode()))
{
//如果之前不存在缓存文件
texture.StopCoroutine("DownloadImage");//UITexture.StopCoroutine可以停止之前的已经无用的协程
texture.StartCoroutine(DownloadImage(url, texture, action, needCache));
}
// 从本地加载
else
{
texture.StopCoroutine("LoadLocalImage");
texture.StartCoroutine(LoadLocalImage(imageCacheRootPath + url.GetHashCode(), texture, action, needCache));
}
}
///
/// 加载本地图片。缓存到内存里。
///
/// 本地文件绝对路径
/// UITexture
/// 加载完成时的回调
public void SetLocalImage(string path, UITexture texture, Action action = null)
{
if (string.IsNullOrEmpty(path))
{
Debug.LogWarning("[SetLocalImage] path is null.");
return;
}
// 从内存获取
if (getFromCache(path, texture))
{
if (action != null)
{
action();
}
return;
}
// 先判断该文件是否存在,不存在直接返回(这样会显示默认图片);如果不做这个判断进入LoadLocalImage,界面上会显示成红色问号。
FileInfo fi = new FileInfo(path);
if (!fi.Exists)
{
Debug.LogError("Path is Not Exists! " + path);
return;
}
texture.StopCoroutine("LoadLocalImage");
texture.StartCoroutine(LoadLocalImage(path, texture, action));
}
///
/// 清空指定key的缓存
///
///
public void clearCache(string filePath)
{
if (textureCache.ContainsKey(filePath))
{
textureCache.Remove(filePath);
}
}
///
///
///
/// 图片url
/// UITexture
/// 加载完成时的回调
/// 是否需要缓存到内存中
///
IEnumerator DownloadImage(string url, UITexture texture, Action action = null, bool needCache = true)
{
int filename = url.GetHashCode();
Debug.Log("[DownloadImage]downloading new image : " + filename);
yield return new WaitForSeconds(0.008f);//如果界面频繁快速切换,之前的图片根本不需要加载,所以这里稍微延迟一会儿再请求网络。
WWW www = new WWW(url);
yield return www;
if (www.error != null)
{
Debug.LogWarning("[DownloadImage] www.error : " + www.error);
if (action != null)
{
action();
}
}
else
{
Debug.Log("[DownloadImage]download ok : " + filename);
Texture2D image = www.texture;
// The data must be an image in JPG or PNG format. If the data is not a valid image, the generated texture will be a small image of a question mark.(红色问号,大小为8*8)
if (image == null || (image.width == 8 && image.height == 8))//如果image为空,或者为红色问号(无效图片)。
{
Debug.LogError("[DownloadImage]www.texture is null or a question mark, return.");
if (action != null)
{
action();
}
}
else
{
image.wrapMode = TextureWrapMode.Clamp;//将Texture的循环模式设为拉伸,Texture边界设为拉伸就不会出现白线。默认是平铺,如果是平铺的话,可能会出现两张图片交界处显示白线的问题。
texture.mainTexture = image;
if (needCache)
{
textureCache[imageCacheRootPath + url.GetHashCode()] = new WeakReference(image);
}
if (action != null)
{
action();
}
//将图片保存至缓存路径
byte[] pngData = www.bytes;
Debug.Log("[DownloadImage] " + filename + " , size : " + (pngData.Length / 1024) + "KB");
File.WriteAllBytes(imageCacheRootPath + filename, pngData);
}
}
}
IEnumerator LoadLocalImage(string filePath, UITexture texture, Action action = null, bool needCache = true)
{
Debug.Log("getting local image :" + filePath);
yield return new WaitForSeconds(0.008f);
WWW www = new WWW("file:///" + filePath);
yield return www;
if (www.error != null)
{
Debug.LogWarning("[LoadLocalImage] www.error : " + www.error);
DeleteFile(filePath);
}
else
{
Texture2D image = www.texture;
//The data must be an image in JPG or PNG format. If the data is not a valid image, the generated texture will be a small image of a question mark.(红色问号,大小为8*8)
if (image == null || (image.width == 8 && image.height == 8))//如果image为空,或者为红色问号(无效图片)。
{
Debug.LogError("www.texture is null or a question mark, delete it.");
DeleteFile(filePath);
}
else
{
image.wrapMode = TextureWrapMode.Clamp;//将Texture的循环模式设为拉伸,Texture边界设为拉伸就不会出现白线。默认是平铺,如果是平铺的话,可能会出现两张图片交界处显示白线的问题。
//直接贴图
texture.mainTexture = image;
if (needCache)
{
textureCache[filePath] = new WeakReference(image);
}
}
}
if (action != null)
{
action();
}
}
///
/// 从textureCache读取
///
///
///
///
private bool getFromCache(string localPath, UITexture texture)
{
if (textureCache.ContainsKey(localPath))
{
WeakReference wr = textureCache[localPath];
if (wr != null && wr.IsAlive)
{
texture.mainTexture = (Texture2D)wr.Target;
if (texture.mainTexture == null)// 有可能在获取到的瞬间被回收了
{
return false;
}
return true;
}
}
return false;
}
private void DeleteFile(string filePath)
{
FileInfo fi = new FileInfo(filePath);
if (fi.Exists)
{
Debug.LogWarning("[DeleteFile] delete this file : " + filePath);
fi.Delete();
}
}
}