如果每个瓦片图每次打开都要重新加载,会比较浪费资源,而且如果网络不好,甚至要等很久,于是可以使用内存缓存,每次已经加载的瓦片图,第二次再加载之前,先看看内存中有没有,这就是内存缓存技术。但是软件已关闭,内存数据全部清空了,下次再打开,又要重新加载,比较浪费资源,于是可以使用本地文件缓存技术,也就是离线地图。
///
/// 缓存管理
///
internal class CacheManager : IDisposable
{
///
/// 默认缓存一个G
///
public int MemoryMaxCacheSize = 1000000;
///
/// 当前已经使用的内存缓存大小
///
public long CurrentMemoryCacheSize = 0;
///
/// 缓存
///
private HashSet LocalCache = new HashSet();
///
/// 内存缓存
///
private IntPtr MemoryCache = IntPtr.Zero;
///
/// 是否已经释放
///
private bool isDisposed = false;
///
/// 内存缓存数据缓存区域
///
private Dictionary MemoryCacheDataRange = new Dictionary();
///
/// 缓存文件夹
///
public string CacheFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "map_caches");
///
/// 是否开启本地文件缓存
///
private bool IsOpenLocalCache = true;
///
/// 是否开启内存缓存
///
private bool IsOpenMemoryCache = true;
///
/// 内存缓存是否已经满了
///
private bool IsMemoryFull = false;
///
/// 锁
///
private object MemoryLocked = new object();
private object LocalLocked = new object();
///
/// 构造函数
///
/// 内存缓存大小
/// 内存缓存单位
/// 是否开启文件缓存
/// 是否开启内存缓存
public CacheManager(int size, MemorySizeUnit unit, bool isOpenLocalCache, bool isOpenMemoryCache)
{
switch (unit)
{
case MemorySizeUnit.GB:
MemoryMaxCacheSize = size * 1024 * 1024 * 1024;
break;
case MemorySizeUnit.MB:
MemoryMaxCacheSize = size * 1024 * 1024;
break;
case MemorySizeUnit.KB:
MemoryMaxCacheSize = size * 1024;
break;
default:
break;
}
IsOpenLocalCache = isOpenLocalCache;
IsOpenMemoryCache = isOpenMemoryCache;
MemoryCache = Marshal.AllocHGlobal(MemoryMaxCacheSize);
InitCacheMode();
}
///
/// 析构函数
///
~CacheManager()
{
Dispose(false);
}
///
/// 初始化缓存模型
///
public void InitCacheMode()
{
if (IsOpenLocalCache)
{
Debug.WriteLine("地图开启缓存模式...");
// 如果开启缓存模式
if (!Directory.Exists(CacheFolder))
{
Directory.CreateDirectory(CacheFolder);
}
var dirs = Directory.GetDirectories(CacheFolder);
foreach (var dir in dirs)
{
foreach (var file in Directory.GetFiles(dir))
{
LocalCache.Add(file);
}
}
}
}
///
/// 读取或者添加一个内存缓存
///
///
///
///
///
public unsafe (MemoryRange, bool) ReadOrSetFromMemoryCache(string name, System.Drawing.Bitmap map = null)
{
lock (MemoryLocked)
{
if (map == null)
{
MemoryRange memory = null;
MemoryCacheDataRange.TryGetValue(name, out memory);
return (memory, true);
}
else
{
if(IsMemoryFull)
{
return (null, false);
}
var size = map.Width * map.Height * 3;
if(CurrentMemoryCacheSize + size > MemoryMaxCacheSize)
{
IsMemoryFull = true;
return (null, false);
}
var range = new MemoryRange(CurrentMemoryCacheSize, CurrentMemoryCacheSize + size, MemoryCache + (int)CurrentMemoryCacheSize);
var mapData = map.LockBits(new System.Drawing.Rectangle(new System.Drawing.Point(), map.Size), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
Buffer.MemoryCopy(mapData.Scan0.ToPointer(), range.Address.ToPointer(), range.Length, (int)range.Length);
map.UnlockBits(mapData);
MemoryCacheDataRange.Add(name, range);
CurrentMemoryCacheSize += size;
return (range, true);
}
}
}
///
/// 读取或者添加一个文件缓存
///
///
///
///
///
public bool ReadOrSetFromLocalCache(string name, bool isGet)
{
lock (LocalLocked)
{
if (isGet)
{
if (LocalCache.Contains(name))
{
return true;
}
return false;
}
else
{
// 如果已经存在本地了 则不需要重新添加
if (LocalCache.Contains(name))
return false;
LocalCache.Add(name);
return true;
}
}
}
///
/// 获取缓存文件路径
///
///
///
private string GetCacheFilePath(TitleBlock titleBlock)
{
var dir = Path.Combine(CacheFolder, titleBlock.Level.ToString());
if (Directory.Exists(dir) is false) Directory.CreateDirectory(dir);
var fileName = string.Format("{0}\\{1}", dir, titleBlock.ToCacheFileName());
return fileName;
}
///
/// 读取数据
///
///
///
public async Task ReadTitleData(TitleBlock block)
{
MemoryRange address = null;
string memoryCacheName = block.ToCacheKey(); //内存缓存名称
string localCacheName = GetCacheFilePath(block); //本地文件缓存名称
if (IsOpenMemoryCache)
{
address = ReadOrSetFromMemoryCache(memoryCacheName).Item1;
}
// 如果从内存中直接读取出来则直接返回
if (address != null) return address;
System.Drawing.Bitmap bitmap = null;
// 如果 开启了文本本地缓存,并且文件确实在本地有缓存
if (IsOpenLocalCache && ReadOrSetFromLocalCache(localCacheName, true))
{
// 从文件中读取数据
bitmap = ReadDataFromLocalFile(localCacheName);
}
// 如果文件中不存在 进行下载
if(bitmap == null)
{
bitmap = await ReadDatasFromHttp(block.Url, localCacheName);
}
// 存入内存
if (bitmap != null && IsOpenMemoryCache)
{
// 存入内存
var res = ReadOrSetFromMemoryCache(memoryCacheName, bitmap);
// 如果保存成功了
if (res.Item2)
{
address = res.Item1;
bitmap.Dispose();
}
else
{
address = new MemoryRange(0, bitmap.Width * 3 * bitmap.Height, IntPtr.Zero) { Bitmap = bitmap };
}
}
// 如果获取到图片并且不需要内存缓存则直接使用
else if (bitmap != null && !IsOpenMemoryCache && address == null)
{
address = new MemoryRange(0, bitmap.Width * 3 * bitmap.Height, IntPtr.Zero) { Bitmap = bitmap };
}
return address;
}
///
/// 转换位图
///
///
///
///
///
private System.Drawing.Bitmap ConvertToBmpDatas(MemoryStream jpegDatas, bool isSaveToLocal, string localPath)
{
System.Drawing.Bitmap jpeg = System.Drawing.Image.FromStream(jpegDatas) as System.Drawing.Bitmap;
if (isSaveToLocal)
{
jpeg.Save(localPath, System.Drawing.Imaging.ImageFormat.Bmp);
}
return jpeg;
}
///
/// 从http中读取数据
///
///
///
///
private async Task ReadDatasFromHttp(Uri url, string localCacheName)
{
System.Drawing.Bitmap bitmap = null;
int tryTimes = 10;
using (HttpClient Client = new HttpClient())
{
while (tryTimes-- > 0 && bitmap == null)
{
try
{
var res = await Client.GetAsync(url);
if (res.StatusCode == System.Net.HttpStatusCode.OK)
{
var stream = await res.Content.ReadAsStreamAsync() as MemoryStream;
// 读取数据并且检测是否保存到本地文件
bitmap = ConvertToBmpDatas(stream, IsOpenLocalCache, localCacheName);
// 添加一个本地的文件缓存信息
if (IsOpenLocalCache)
{
ReadOrSetFromLocalCache(localCacheName, false);
}
if (bitmap != null) return bitmap;
}
}
catch (Exception ex) { }
}
}
return bitmap;
}
///
/// 从本地文件缓存中加载数据
///
/// 本地缓存文件名称
///
private Bitmap ReadDataFromLocalFile(string localCacheName)
{
try
{
var bitmap = new System.Drawing.Bitmap(localCacheName);
if (bitmap == null || bitmap.Width == 0 || bitmap.Height == 0)
{
if (bitmap != null) bitmap.Dispose();
bitmap = null;
}
return bitmap;
}
catch (Exception ex)
{
return null;
}
}
///
/// 释放
///
public void Dispose()
{
Dispose(true);
}
public void Dispose(bool dispose)
{
if (isDisposed)
{
return;
}
if (dispose)
{
// 释放托管的 IDispose 对象
}
// 释放非托管内容
Marshal.FreeHGlobal(MemoryCache);
isDisposed = true;
}
}
获取瓦片的时候
///
/// 读取数据
///
///
public Task ReadStream() => CacheManager.ReadTitleData(this);