关闭 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

    返回首页 下一篇

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

    别人在看

    抖音安全与信任开放日:揭秘推荐算法,告别单一标签依赖

    ultraedit编辑器打开文件时,总是提示是否转换为DOS格式,如何关闭?

    Cornell大神Kleinberg的经典教材《算法设计》是最好入门的算法教材

    从 Microsoft 下载中心安装 Windows 7 SP1 和 Windows Server 2008 R2 SP1 之前要执行的步骤

    Llama 2基于UCloud UK8S的创新应用

    火山引擎DataTester:如何使用A/B测试优化全域营销效果

    腾讯云、移动云继阿里云降价后宣布大幅度降价

    字节跳动数据平台论文被ICDE2023国际顶会收录,将通过火山引擎开放相关成果

    这个话题被围观超10000次,火山引擎VeDI如此解答

    误删库怎么办?火山引擎DataLeap“3招”守护数据安全

    IT头条

    平替CUDA!摩尔线程发布MUSA 4性能分析工具

    00:43

    三起案件揭开侵犯个人信息犯罪的黑灰产业链

    13:59

    百度三年开放2.1万实习岗,全力培育AI领域未来领袖

    00:36

    工信部:一季度,电信业务总量同比增长7.7%,业务收入累计完成4469亿元

    23:42

    Gartner:2024年全球半导体营收6559亿美元,AI助力英伟达首登榜首

    18:04

    技术热点

    iOS 8 中如何集成 Touch ID 功能

    windows7系统中鼠标滑轮键(中键)的快捷应用

    MySQL数据库的23个特别注意的安全事项

    Kruskal 最小生成树算法

    Ubuntu 14.10上安装新的字体图文教程

    Ubuntu14更新后无法进入系统卡在光标界面解怎么办?

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

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