关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » 安卓开发 »自己封装双缓存管理框架 Android 库

    自己封装双缓存管理框架 Android 库

    2015-12-12 00:00:00 出处:freebuf
    分享

    一、概述

    Android开发中,网络请求是很重要的一部分,而缓存网络请求来的图片或者响应结果字符串或者结果流,既可以省流量,同时也可以帮助我们解决无网或弱网情况下加载情况,当然也可以提升程序性能效率。纵所周知,缓存管理中肯定需要用到内存缓存,这里我们采用LruCache来管理内存的缓存。

    LruCahce虽然速度快,但是只是内存级别的缓存,为了实现持久化的缓存,我们还需要文件级别的缓存,也就是说大家要把缓存保存到文件,而文件则是保存到手机存储或者SD卡存储中,即实现Disk级别的缓存,这里我们借助DiskLruCache这个辅助工具类来实现。顾名思义,这个工具类的作用就是使用Lru算法来存储信息到Disk上。

    二、实例效果图

    下面是个简单的实例演示效果图

    自己封装双缓存管理框架Android 库

    三、缓存管理框架的实现解

    1、内存缓存类的实现

    该类主要实现内存级别的缓存管理类MemoryCache,使用LruCache来实现,因为无论是内存缓存还是Disk缓存,都需要读写操作,所以我们先抽象出一个缓存接口类:Cache接口:

    public interface Cache {
        String get(final String key);
        void put(final String key, final String value);
        boolean remove(final String key);
    }

    然后,我们的内存缓存类MemoryCache需要实现Cache这个接口:

    /**
     * 内存缓存类
     * Created by caizhiming on 2015/12/4.
     */
    public class MemoryCache implements Cache {
        private LruCache<String, String> mMemoryLruCache;
        private EvictedListener mEvictedListener;
    
        public MemoryCache() {
            init();
        }
    
        public MemoryCache(EvictedListener listener) {
            init();
            this.mEvictedListener = listener;
        }
    
        public void setEvictedListener(EvictedListener listener) {
            this.mEvictedListener = listener;
        }
    
        public boolean hasEvictedListener() {
            return mEvictedListener != null;
        }
    
        private void init() {
            // 计算可使用的最大内存
            final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
            // 取可用内存空间的1/4作为缓存
            final int cacheSize = maxMemory / 4;
            mMemoryLruCache = new LruCache<String, String>(cacheSize) {
                @Override
                protected int sizeOf(String key, String value) {
                    return value.getBytes().length;
                }
    
                @Override
                protected void entryRemoved(boolean evicted, String key, String oldValue, String newValue) {
                    if (evicted) {
                        if (mEvictedListener != null) {
                            mEvictedListener.handleEvictEntry(key, oldValue);
                        }
                    }
                }
            };
        }
    
        @Override
        public String get(String key) {
            return mMemoryLruCache.get(key);
        }
    
        @Override
        public void put(String key, String value) {
            mMemoryLruCache.put(key, value);
        }
    
        @Override
        public boolean remove(String key) {
            return Boolean.parseBoolean(mMemoryLruCache.remove(key));
        }
    
        /**
         * called when mMemoryLruCache evict entrys,
         * <p/>
         * using by CacheManager.Strategy.MEMORY_FIRST
         */
        public interface EvictedListener {
            void handleEvictEntry(String evictKey, String evictValue);
        }

    2、文件级别的Disk缓存类实现

    接下来我们需要写一个用于Disk缓存管理的类:DiskCache类,该类我们也实现Cache接口,该类的主要功能也是提供Disk缓存的读取和写入操作管理。

    /**
     * Disk磁盘缓存类
     * Created by caizhiming on 2015/12/4.
     */
    public class DiskCache implements Cache{
    
        private DiskLruCache mDiskLruCache = null;
        public DiskCache(Context context){
            init(context);
        }
        /**
         * 初始化 DiskLruCache
         */
        public void init(Context context){
            try {
                File cacheDir = getDiskCacheDir(context, "http_cache");
                if (!cacheDir.exists()) {
                    cacheDir.mkdirs();
                }
                Log.v("czm", "cache file=" + cacheDir.getAbsolutePath());
                mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public String get(String key) {
            String result = null;
            try {
                DiskLruCache.Snapshot snapShot = mDiskLruCache.get(hashKeyForDisk(key));
                if (snapShot != null) {
                    result = snapShot.getString(0);
                    return result;
                }
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
            return result;
        }
    
        @Override
        public void put(String key, String value) {
            DiskLruCache.Editor editor = null;
            try {
                editor = mDiskLruCache.edit(hashKeyForDisk(key));
                if (editor != null) {
                    editor.set(0, value);
                    editor.commit();
                }
                mDiskLruCache.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public boolean remove(String key) {
            try {
                return mDiskLruCache.remove(hashKeyForDisk(key));
            } catch (IOException e) {
                e.printStackTrace();
            }
            return false;
        }
    
        public Bitmap getImageCache(String key){
            Bitmap bitmap = null;
            try {
                DiskLruCache.Snapshot snapShot = mDiskLruCache.get(hashKeyForDisk(key));
                if (snapShot != null) {
                    InputStream is = snapShot.getInputStream(0);
                    bitmap = BitmapFactory.decodeStream(is);
                    return bitmap;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return bitmap;
        }
        public void putImageCache(final String key){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        DiskLruCache.Editor editor = mDiskLruCache.edit(hashKeyForDisk(key));
                        if (editor != null) {
                            OutputStream outputStream = editor.newOutputStream(0);
                            if (downloadUrlToStream(key, outputStream)) {
                                editor.commit();
                            } else {
                                editor.abort();
                            }
                        }
                        mDiskLruCache.flush();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
            HttpURLConnection urlConnection = null;
            BufferedOutputStream out = null;
            BufferedInputStream in = null;
            try {
                final URL url = new URL(urlString);
                urlConnection = (HttpURLConnection) url.openConnection();
                in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
                out = new BufferedOutputStream(outputStream, 8 * 1024);
                int b;
                while ((b = in.read()) != -1) {
                    out.write(b);
                }
                return true;
            } catch (final IOException e) {
                e.printStackTrace();
            } finally {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }
                CloseUtils.closeCloseable(out);
                CloseUtils.closeCloseable(in);
            }
            return false;
        }
    
        public String hashKeyForDisk(String key) {
            String cacheKey;
            try {
                final MessageDigest mDigest = MessageDigest.getInstance("MD5");
                mDigest.update(key.getBytes());
                cacheKey = bytesToHexString(mDigest.digest());
            } catch (NoSuchAlgorithmException e) {
                cacheKey = String.valueOf(key.hashCode());
            }
            return cacheKey;
        }
    
        private String bytesToHexString(byte[] bytes) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < bytes.length; i++) {
                String hex = Integer.toHexString(0xFF & bytes[i]);
                if (hex.length() == 1) {
                    sb.append('0');
                }
                sb.append(hex);
            }
            return sb.toString();
        }
        public File getDiskCacheDir(Context context, String uniqueName) {
            String cachePath;
            if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                    || !Environment.isExternalStorageRemovable()) {
                cachePath = context.getExternalCacheDir().getPath();
            } else {
                cachePath = context.getCacheDir().getPath();
            }
            return new File(cachePath + File.separator + uniqueName);
        }
    
        public int getAppVersion(Context context) {
            try {
                PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
                return info.versionCode;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return 1;
        }
    
    }

    3、搭建封装双缓存管理框架类XCCacheManager

    有了上面的内存缓存类MemoryCache和Disk缓存类DiskCache,我们就可以搭建封装真正的缓存管理类XCCacheManager了。

    (1) 首先我们采用线程池技术来实现多线程缓存的读写操作

    这样可以提高程序的性能,同时能处理任务量比较大的并发读写操作。

    private static XCCacheManager mInstance = null;
    
    private Strategy mStrategy = Strategy.MEMORY_FIRST;
    //线程池
    private ExecutorService mExecutor = null;
    //内存缓存
    private MemoryCache mMemoryCache;
    //Disk缓存
    private DiskCache mDiskCache;
    
    /**
         * 初始化 DiskLruCache
         */
        private void init(Context context) {
            mExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
            mDiskCache = new DiskCache(context);
            mMemoryCache = new MemoryCache();
        }

    (2)其次XCCacheManager管理类采用单例实现

    public static XCCacheManager getInstance(Context context, Strategy strategy) {
        if (mInstance == null) {
            synchronized (XCCacheManager.class) {
                if (mInstance == null) {
                    mInstance = new XCCacheManager(context.getApplicationContext(), strategy);
                }
            }
        } else {
            mInstance.setStrategy(strategy);
        }
        return mInstance;
    }

    (3)缓存策略

    这里我们定义了缓存策略,便于适应各种不同业务需求,可以灵活使用不同的策略

    enum Strategy {
            MEMORY_ONLY(0), MEMORY_FIRST(1), DISK_ONLY(3);
            int id;
    
            Strategy(int id) {
                this.id = id;
            }
        }

    默认采用内存优先MEMORY_FIRST这种策略

    (4)根据对应的策略从缓存中读取内容

    /**
         * 从缓存中读取value
         */
        public String readCache(final String key) {
            Future<String> ret = mExecutor.submit(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    String result = null;
                    switch (mStrategy) {
                        case MEMORY_ONLY:
                            result = mMemoryCache.get(key);
                            break;
                        case MEMORY_FIRST:
                            result = mMemoryCache.get(key);
                            if (result == null) {
                                result = mDiskCache.get(key);
                            }
                            break;
                        case DISK_ONLY:
                            result = mDiskCache.get(key);
                            break;
                    }
                    return result;
                }
            });
            try {
                return ret.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            return null;
        }

    (5)将内容写入到缓存中

    /**
         * 将value 写入到缓存中
         */
        public void writeCache(final String key, final String value) {
            mExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    switch (mStrategy) {
                        case MEMORY_FIRST:
                            if (!mMemoryCache.hasEvictedListener()) {
                                mMemoryCache.setEvictedListener(new MemoryCache.EvictedListener() {
                                    @Override
                                    public void handleEvictEntry(String evictKey, String evictValue) {
                                        mDiskCache.put(evictKey, evictValue);
                                    }
                                });
                            }
                            mMemoryCache.put(key, value);
                            break;
                        case MEMORY_ONLY:
                            if (mMemoryCache.hasEvictedListener())
                                mMemoryCache.setEvictedListener(null);
                            mMemoryCache.put(key, value);
                            break;
                        case DISK_ONLY:
                            mDiskCache.put(key, value);
                            break;
                    }
                }
            });
        }

    到此为止,框架的开发到此完成。希望对有需要的人有所帮助。

    四、源码下载

    源码下载 : http://download.csdn.net/detail/jczmdeveloper/9348031

    GitHub地址:https://github.com/jczmdeveloper/XCCacheManager

    返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    Destoon 模板存放规则及语法参考

    Destoon系统常量与变量

    Destoon系统目录文件结构说明

    Destoon 系统安装指南

    Destoon会员公司主页模板风格添加方法

    Destoon 二次开发入门

    Microsoft 将于 2026 年 10 月终止对 Windows 11 SE 的支持

    Windows 11 存储感知如何设置?了解Windows 11 存储感知开启的好处

    Windows 11 24H2 更新灾难:系统升级了,SSD固态盘不见了...

    小米路由器买哪款?Miwifi热门路由器型号对比分析

    IT头条

    Synology 对 Office 套件进行重大 AI 更新,增强私有云的生产力和安全性

    01:43

    StorONE 的高效平台将 Storage Guardian 数据中心占用空间减少 80%

    11:03

    年赚千亿的印度能源巨头Nayara 云服务瘫痪,被微软卡了一下脖子

    12:54

    国产6nm GPU新突破!砺算科技官宣:自研TrueGPU架构7月26日发布

    01:57

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    技术热点

    如何删除自带的不常用应用为windows 7减负

    MySQL中多表删除方法

    改进的二值图像像素标记算法及程序实现

    windows 7 32位系统下手动修改磁盘属性例如M盘修改为F盘

    windows 7中怎么样在家庭组互传文件

    Linux应用集成MySQL数据库访问技巧

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。