美文网首页
Glide 缓存原理实现

Glide 缓存原理实现

作者: 他晓 | 来源:发表于2020-03-24 21:34 被阅读0次

    Glide 缓存原理实现

    专注于Android开发,分享经验总结,欢迎加入
    QQ群:686809487

    Glide使用方式如下:
    
    Glide.with(MainActivity.this)
                        .load(path)
                        .into(iv);
    

    Glide缓存分为:活动缓存、内存缓存、Bitmap复用池、磁盘缓存、加载外置(网络或者SD卡)、绑定生命周期
    具体的流程如下

    在这里插入图片描述
    • 资源封装

      Key -- 对Value的唯一性进行描述

      Value -- 对Bitmap的封装

    • 活动缓存

      // 用容器存储

      private HashMap<String, WeakReference<Value>> map = new HashMap<>();

        // 继承WeakReference(目的:为了监听这个弱引用 是否被回收了)
        public class CustomWeakReference extends WeakReference {
            public String key;
    
            public CustomWeakReference(Object referent, ReferenceQueue queue, String key) {
                super(referent, queue);
                this.key = key;
            }
        }
    
         /**
         * 目的:为了监听这个弱引用 是否被回收了
         * queue.remove() 会阻塞线程
         *
         * @return
         */
        public ReferenceQueue<Value> getQueue() {
            if (queue == null) {
                queue = new ReferenceQueue<>();
                // 监听这个弱引用 是否被回收了
                thread = new Thread() {
                    @Override
                    public void run() {
                        while (!isCloseThread) {
                            if (!isShoudonRemove) {
                                try {
                                    // 阻塞式的方法,被动调用queue.remove(),进行回收
                                    Reference<? extends Value> remove = queue.remove();
                                    CustomWeakReference customWeakReference = (CustomWeakReference) remove;
                                    if (map != null && !map.isEmpty()) {
                                        // 移除容器 !isShoudonRemove:为了区分手动移除 和 被动移除
                                        map.remove(customWeakReference.key);
                                    }
                                    Log.d(TAG, "getQueue remove ");
                                } catch (InterruptedException e) {
                                    Log.d(TAG, "getQueue InterruptedException e :" + e.getMessage());
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                };
                thread.start();
            }
            return queue;
        }
    > 当活动缓存的值不在被使用时,从活动缓存移除,并加入都内存缓存
    
    • 内存缓存

      内存缓存 使用 Lru算法,继承自 LruCache<String, Value>

      重写sizeOf(),entryRemoved()方法监听元素被移除

        @Override
        protected int sizeOf(@NonNull String key, @NonNull Value value) {
            Bitmap bitmap = value.getBitmap();
            // 最开始的时候
            // int result = bitmap.getRowBytes() * bitmap.getHeight();
      
            // API 12  3.0
            // result = bitmap.getByteCount(); // 在bitmap内存复用上有区别 (所属的)
      
            // API 19 4.4
            // result = bitmap.getAllocationByteCount(); // 在bitmap内存复用上有区别 (整个的)
            return Tool.getBitmapByteSize(bitmap);
        }
      
         /**
         * 被移除时监听
         * 1.重复的key
         * 2.最少使用的元素会被移除
         *
         * @param evicted
         * @param key
         * @param oldValue
         * @param newValue
         */
        @Override
        protected void entryRemoved(boolean evicted, String key, Value oldValue, Value newValue) {
            super.entryRemoved(evicted, key, oldValue, newValue);
            // !shoudonRemove == 被动的
            if (memoryCacheCallback != null && !shoudonRemove) {
                memoryCacheCallback.entryRemovedMemoryCache(key, oldValue);
            }
        }
      

      当内存缓存移除最少使用值的时候,加入到BitmapPool复用池(复用Bitmap地址,避免频繁调用内存空间,防止内存抖动、内存碎片)

    • Bitmap复用池

      LruBitmapPool extends LruCache<Integer, Bitmap>

      使用容器TreeMap<Integer, Bitmap> treeMap = new TreeMap<>();是为了筛选

      重写put()和get()方法,加入存取的一些条件

        @Override
        public void put(Bitmap bitmap) {
            Tool.checkNotEmpty(bitmap);
            // TODO 复用的条件1 bitmap.isMutable()
            if (!bitmap.isMutable()) {
                if (!bitmap.isRecycled()) {
                    bitmap.recycle();
                    Log.d(TAG, "put: 复用的条件1 Bitmap.ismutable 是false,条件不满足,不能复用 添加..." + bitmap);
                    return;
                }
            }
            // TODO 复用的条件2 如果添加复用的Bitmap大小,大于Lru MaxSize 就不复用
            int bitmapByteSize = Tool.getBitmapByteSize(bitmap);
            if (bitmapByteSize > maxSize()) {
                if (!bitmap.isRecycled()) {
                    bitmap.recycle();
                }
                Log.d(TAG, "put: 复用的条件2 Bitmap.Size大于LruMaxSize,条件不满足,不能复用 添加...");
                return;
            }
            // 添加到 Lru Cahce中去
            put(bitmapByteSize, bitmap);
            // 保存到 TreeMap 是为了筛选
            treeMap.put(bitmapByteSize, null);
            Log.d(TAG, "put: 添加到复用池了....");
        }
      
        @Override
        public Bitmap get(int width, int height, Bitmap.Config config) {
            if (treeMap.isEmpty()) {
                Log.d(TAG, "treeMap:为空");
                return null;
            }
            // config为null 默认给 Bitmap.Config.ARGB_8888
            int bitmapByteSize = Tool.getBitmapByteSize(width, height, config);
            // TODO ceilingKey 获得 getSize这么大的key,同时还可以获得 比 getSize还要大的key
            Integer key = treeMap.ceilingKey(bitmapByteSize);// 获得 getSize这么大的key,同时还可以获得 比 getSize还要大的key
            if (key == null) {
                Log.d(TAG, "treeMap:找不到 保存的key");
                return null; // 如果找不到 保存的key,就直接返回null,无法复用
            }
            // 找出来的key 小于等于 (getSize * 2)
            if (key <= (bitmapByteSize * 2)) {
                Bitmap resultBitmap = remove(key);
                Log.d(TAG, "get: 从复用池获取:" + resultBitmap);
                return resultBitmap;
            }
            return null;
        }
      

      BitmapFactory.Options options = new BitmapFactory.Options();

      options.inBitmap = bitmapPoolResult;// bitmapPoolResult为null,不复用地址,复用Bitmap地址,避免频繁调用内存空间,防止内存抖动、内存碎片

    • 磁盘缓存

      使用JakeWharton开源项目DiskLruCache

      https://github.com/JakeWharton/DiskLruCache

        // TODO put 存入Value
        public void put(String key, Value value) {
            Tool.checkNotEmpty(key);
            OutputStream outputStream = null;
            DiskLruCache.Editor edit = null;
            try {
                edit = diskLruCache.edit(key);
                // index 不能大于 VALUE_COUNT
                outputStream = edit.newOutputStream(0);
                // 把bitmap写入到outputStream
                Bitmap bitmap = value.getBitmap();
                if (bitmap != null) {
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
                    outputStream.flush();
                }
            } catch (IOException e) {
                e.printStackTrace();
                try {
                    // Aborts this edit. This releases the edit lock so another edit may be
                    // 中止此编辑。 这会释放编辑锁,因此可能需要进行其他编辑
                    if (edit != null) {
                        edit.abort();
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                    Log.e(TAG, "put: editor.abort(); e:" + ex.getMessage());
                }
            } finally {
                try {
                    if (edit != null) {
                        edit.commit();
                    }
                    diskLruCache.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e(TAG, "put: editor.commit(); e:" + e.getMessage());
                }
                if (outputStream != null) {
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                        Log.e(TAG, "put: outputStream.close(); e:" + e.getMessage());
                    }
                }
      
            }
        }
      
        // TODO get 获取Value
        public Value get(String key, BitmapPool bitmapPool) {
            InputStream inputStream = null;
            try {
                DiskLruCache.Snapshot snapshot = diskLruCache.get(key);
                // 判断快照不为null的情况下,在去读取操作
                if (snapshot != null) {
                    // index 不能大于 VALUE_COUNT
                    inputStream = snapshot.getInputStream(0);
                    // 复用Bitmap地址,避免频繁调用内存空间,防止内存抖动、内存碎片
                    Bitmap bitmap = Tool.getIOBitmap(inputStream, bitmapPool, true);
      
                    Value value = Value.getInstance();
                    value.setKey(key);
                    value.setBitmap(bitmap);
                    return value;
                }
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(TAG, "get: IOException; e:" + e.getMessage());
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                        Log.e(TAG, "get: inputStream.close(); e:" + e.getMessage());
                    }
                }
            }
            return null;
        }
      
    • 生命周期

      .with(Context) 与当前的上下文绑定;通过上下文绑定一个Fragment实现管理生命周期

      Context分为:

      1. Application无法绑定生命周期

      2. FragmentActivity 可绑定生命周期

      3. Activity 可绑定生命周期

         public interface LifecycleCallback {
              // 生命周期初始化了
              public void glideInitAction();
        
              // 生命周期 停止了
              public void glideStopAction();
        
             // 生命周期 释放 操作了
             public void glideRecycleAction();
         }
        
    • 网络加载/SD卡加载

      网络加载:开一个线程池去加载,通过HttpConnection

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
        threadPoolExecutor.execute(this);
        
        开子线程去加载
        @Override
        public void run() {
            InputStream inputStream = null;
            HttpURLConnection httpURLConnection = null;
            try {
                URL url = new URL(path);
                URLConnection urlConnection = url.openConnection();
                httpURLConnection = (HttpURLConnection) urlConnection;
                httpURLConnection.setReadTimeout(5000);
                final int responseCode = httpURLConnection.getResponseCode();
                if (HttpURLConnection.HTTP_OK == responseCode) {
                    inputStream = httpURLConnection.getInputStream();
                    // 复用Bitmap地址,避免频繁调用内存空间,防止内存抖动、内存碎片
                    final Bitmap bitmap = Tool.getIOBitmap(inputStream, null, false);
      
                    new Handler(Looper.getMainLooper()).post(new Runnable() {
                        @Override
                        public void run() {
                            Value value = Value.getInstance();
                            value.setBitmap(bitmap);
                            responseListener.responseSuccess(value);
                        }
                    });
                } else {
                    // 失败 切换主线程
                    new Handler(Looper.getMainLooper()).post(new Runnable() {
                        @Override
                        public void run() {
                            responseListener.responseException(new IllegalStateException("请求失败 请求码:" + responseCode));
                        }
                    });
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                        e.printStackTrace();
                        Log.d(TAG, "run: 关闭 inputStream.close(); e:" + e.getMessage());
                    }
                }
                if (httpURLConnection != null) {
                    httpURLConnection.disconnect();
                }
            }
        }
      

      SD卡加载:

        Bitmap bitmap = BitmapFactory.decodeFile(path);
        Value value = Value.getInstance();
        value.setBitmap(bitmap);
        // 回调成功
        Log.d(TAG, " LoadDataManager 从本地获取到bitmap");
        responseListener.responseSuccess(value);
      
    • 日志记录

    image

    源码查看,关注公众号回复 Glide ,获取

    微信公众号 -->> 他晓 (欢迎加入)

    相关文章

      网友评论

          本文标题:Glide 缓存原理实现

          本文链接:https://www.haomeiwen.com/subject/peoayhtx.html