zoukankan      html  css  js  c++  java
  • Android 框架修炼-自己封装双缓存管理框架库

    一、概述

      Android开发中,网络请求是很重要的一部分,而缓存网络请求来的图片或者响应结果字符串或者结果流,既可以省流量,同时也可以帮助我们

    解决无网或弱网情况下加载情况,当然也可以提升程序性能效率。纵所周知,缓存管理中肯定需要用到内存缓存,这里我们采用LruCache来管理内存的缓存。

    LruCahce虽然速度快,但是只是内存级别的缓存,为了实现持久化的缓存,我们还需要文件级别的缓存,也就是说我们要把缓存保存到文件,而文件则是保存

    到手机存储或者SD卡存储中,即实现Disk级别的缓存,这里我们借助DiskLruCache这个辅助工具类来实现。顾名思义,这个工具类的作用就是使用Lru算法

    来存储信息到Disk上。

    二、实例效果图

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

         

      

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

    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)缓存策略

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

    
    
    public void setStrategy(XCCacheManager.Strategy strategy) {
    this.mStrategy = strategy;
    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);
    }
    });
    }
    break;
    case MEMORY_ONLY:
    if (mMemoryCache.hasEvictedListener())
    mMemoryCache.setEvictedListener(null);
    break;
    case DISK_ONLY:
    break;
    }
    }
     

    默认采用内存优先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:
    mMemoryCache.put(key, value);
    mDiskCache.put(key,value);
    break;
    case MEMORY_ONLY:
    mMemoryCache.put(key, value);
    break;
    case DISK_ONLY:
    mDiskCache.put(key, value);
    break;
    }
    }
    });
    }
     

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

    四、源码下载

    源码下载: www.demodashi.com/demo/12143.html

     真题园网http://www.zhentiyuan.com

  • 相关阅读:
    一道看似简单的sql需求却难倒各路高手
    MahApps.Metro怎么调用消息窗口
    CodeSmith Generator 7.0.2激活步骤
    8款图表插件推荐
    VS的代码分析工具
    RDLC系列之六 打印纸张的大小(未解决)
    初识python
    应用程序的更新
    Expression<Func<T,TResult>>和Func<T,TResult>
    HTML5 history新特性pushState、replaceState
  • 原文地址:https://www.cnblogs.com/JczmDeveloper/p/5039970.html
Copyright © 2011-2022 走看看