Glide 缓存原理实现

Glide 缓存原理实现

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

QQ群

Glide使用方式如下:

Glide.with(MainActivity.this)
                    .load(path)
                    .into(iv);

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

在这里插入图片描述

  • 资源封装

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

    Value – 对Bitmap的封装

  • 活动缓存

    // 用容器存储

    private HashMap<String, WeakReference> 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);
    
  • 日志记录
    在这里插入图片描述

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

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

公众号

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值