WPF中手写地图控件(4)——离线地图

28 篇文章 3 订阅
4 篇文章 0 订阅

内存缓存和本地文件缓存技术

如果每个瓦片图每次打开都要重新加载,会比较浪费资源,而且如果网络不好,甚至要等很久,于是可以使用内存缓存,每次已经加载的瓦片图,第二次再加载之前,先看看内存中有没有,这就是内存缓存技术。但是软件已关闭,内存数据全部清空了,下次再打开,又要重新加载,比较浪费资源,于是可以使用本地文件缓存技术,也就是离线地图。

/// <summary>
/// 缓存管理
/// </summary>
internal class CacheManager : IDisposable
{
    /// <summary>
    /// 默认缓存一个G
    /// </summary>
    public int MemoryMaxCacheSize = 1000000;
    /// <summary>
    /// 当前已经使用的内存缓存大小
    /// </summary>
    public long CurrentMemoryCacheSize = 0;
    /// <summary>
    /// 缓存
    /// </summary>
    private HashSet<string> LocalCache = new HashSet<string>();
    /// <summary>
    /// 内存缓存
    /// </summary>
    private IntPtr MemoryCache = IntPtr.Zero;
    /// <summary>
    /// 是否已经释放
    /// </summary>
    private bool isDisposed = false;
    /// <summary>
    /// 内存缓存数据缓存区域
    /// </summary>
    private Dictionary<string, MemoryRange> MemoryCacheDataRange = new Dictionary<string, MemoryRange>();
    /// <summary>
    /// 缓存文件夹
    /// </summary>
    public string CacheFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "map_caches");
    /// <summary>
    /// 是否开启本地文件缓存
    /// </summary>
    private bool IsOpenLocalCache = true;
    /// <summary>
    /// 是否开启内存缓存
    /// </summary>
    private bool IsOpenMemoryCache = true;
    /// <summary>
    /// 内存缓存是否已经满了
    /// </summary>
    private bool IsMemoryFull = false;
    /// <summary>
    /// 锁
    /// </summary>
    private object MemoryLocked = new object();
    private object LocalLocked = new object();
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="size">内存缓存大小</param>
    /// <param name="unit">内存缓存单位</param>
    /// <param name="isOpenLocalCache">是否开启文件缓存</param>
    /// <param name="isOpenMemoryCache">是否开启内存缓存</param>
    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();
    }
    /// <summary>
    /// 析构函数
    /// </summary>
    ~CacheManager()
    {
        Dispose(false);
    }
    /// <summary>
    /// 初始化缓存模型
    /// </summary>
    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);
                }
            }
        }
    }

    
    /// <summary>
    /// 读取或者添加一个内存缓存
    /// </summary>
    /// <param name="name"></param>
    /// <param name="memoryData"></param>
    /// <param name="size"></param>
    /// <returns></returns>
    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);
            }
        }
    }

    /// <summary>
    /// 读取或者添加一个文件缓存
    /// </summary>
    /// <param name="name"></param>
    /// <param name="memoryData"></param>
    /// <param name="size"></param>
    /// <returns></returns>
    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;
            }
        }
    }
    /// <summary>
    /// 获取缓存文件路径
    /// </summary>
    /// <param name="block"></param>
    /// <returns></returns>
    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;
    }
    /// <summary>
    /// 读取数据
    /// </summary>
    /// <param name="block"></param>
    /// <returns></returns>
    public async Task<MemoryRange> 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;
    }
    /// <summary>
    /// 转换位图
    /// </summary>
    /// <param name="jpegDatas"></param>
    /// <param name="isSaveToLocal"></param>
    /// <param name="localPath"></param>
    /// <returns></returns>
    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;
    }
    /// <summary>
    /// 从http中读取数据
    /// </summary>
    /// <param name="url"></param>
    /// <param name="localCacheName"></param>
    /// <returns></returns>
    private async Task<System.Drawing.Bitmap> 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;
    }
    /// <summary>
    /// 从本地文件缓存中加载数据
    /// </summary>
    /// <param name="localCacheName">本地缓存文件名称</param>
    /// <returns></returns>
    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;
        }
    }
    /// <summary>
    /// 释放
    /// </summary>
    public void Dispose()
    {
        Dispose(true);
    }

    public void Dispose(bool dispose)
    {
        if (isDisposed)
        {
            return;
        }
        if (dispose)
        {
            //  释放托管的 IDispose 对象
        }
        // 释放非托管内容
        Marshal.FreeHGlobal(MemoryCache);
        isDisposed = true;
    }
}

获取瓦片的时候

/// <summary>
/// 读取数据
/// </summary>
/// <returns></returns>
public Task<MemoryRange> ReadStream() => CacheManager.ReadTitleData(this);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值