首页 > 其他分享 >Android经典面试题之Glide的缓存大揭秘

Android经典面试题之Glide的缓存大揭秘

时间:2024-07-02 23:31:32浏览次数:30  
标签:writeLock 面试题 缓存 Glide -- private Android safeKey final

本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点

Glide缓存

关联类:Engine、LruResourceCache、LruCache、ActiveResources

ActiveResources:弱引用缓存池
@VisibleForTesting final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue = new ReferenceQueue<>();
LruCache:LinkedHashMap缓存池
private final Map<T, Entry<Y>> cache = new LinkedHashMap<>(100, 0.75f, true);

入口:Engine.load方法

先从缓存中取

--> loadFromMemory

--> loadFromActiveResources(弱引用池) 

--> loadFromCache 
--> getEngineResourceFromCache(LRU缓存池,LinkedHashMap)  

LRU缓存池中取到EngineResource后,会从LRU缓存中删除,然后对它引用计数+1,放入弱引用池

缓存中没有找到,就需要创建任务执行

-->  waitForExistingOrStartNewJob  
--> 如果当前图片任务已经有EngineJob了,就直接加个Callback

---> 没有的话就创建EngineJob和DecodeJob从本地加载或者是网络加载

EngineResource通过引用计数来判断是否需要释放资源,释放的资源会从弱引用池中删除,放入LRU缓存中

缓存大小设置

涉及的类:MemorySizeCalculator

  • 首先获取App可用内存大小,Glide的内存大小限制在0.4以下,如果是低内存的系统,则是在0.33
private static int getMaxSize(
	ActivityManager activityManager, float maxSizeMultiplier, float lowMemoryMaxSizeMultiplier) {
	final int memoryClassBytes = activityManager.getMemoryClass() * 1024 * 1024;
	final boolean isLowMemoryDevice = isLowMemoryDevice(activityManager);
	//lowMemoryMaxSizeMultiplier是0.33
	//maxSizeMultiplier是0.4
	return Math.round(
			memoryClassBytes * (isLowMemoryDevice ? lowMemoryMaxSizeMultiplier : maxSizeMultiplier));
}

@TargetApi(Build.VERSION_CODES.KITKAT)
@Synthetic
static boolean isLowMemoryDevice(ActivityManager activityManager) {
	if (Build.VERSION.SDK\_INT >= Build.VERSION\_CODES.KITKAT) {
		return activityManager.isLowRamDevice();
	} else {
		return true;
	}
}
  • 图片缓存大小,用几屏来表示,跟屏幕的分辨率有关
//一屏的图片大小 宽*高*4(ARG888图片的像素大小就是4字节)
int screenSize = widthPixels * heightPixels * BYTES_PER_ARGB_8888_PIXEL;

//BitmapPool最新的是API26以上是4,以下的是1
int targetBitmapPoolSize = Math.round(screenSize * builder.bitmapPoolScreens);

//memoryCache是2屏
int targetMemoryCacheSize = Math.round(screenSize * builder.memoryCacheScreens);
  • LRU缓存动态限制图片缓存大小
 //在低内存回调,或是put新的图片后,都会进行缓存大小检查,如果超过就移除不太用的
protected synchronized void trimToSize(long size) {
	Map.Entry<T, Entry<Y>> last;
	Iterator<Map.Entry<T, Entry<Y>>> cacheIterator;
	while (currentSize > size) {
		cacheIterator = cache.entrySet().iterator();
		last = cacheIterator.next();
		final Entry\<Y> toRemove = last.getValue();
		currentSize -= toRemove.size;
		final T key = last.getKey();
		cacheIterator.remove();
		onItemEvicted(key, toRemove.value);
	}
}
DiskLruCache中的读写锁

写的时候会加锁,这个锁是自定义的,并且有一个锁的池子

 private static class WriteLock {
   final Lock lock = new ReentrantLock();
   int interestedThreads;

   @Synthetic
   WriteLock() {}
}

每次写的时候会加锁,并且会对这个WriteLock的interestedThreads分别在开始写和结束时进行加减操作

writeLocker.acquire(safeKey);
....
writeLocker.release(safeKey);

writeLocker从锁池子里取,key的话是用请求的key做哈希得到

 //SafeKeyGenerator.class
 private String calculateHexStringDigest(Key key) {
     PoolableDigestContainer container = Preconditions.checkNotNull(digestPool.acquire());
     try {
       key.updateDiskCacheKey(container.messageDigest);
       // calling digest() will automatically reset()
       return Util.sha256BytesToHex(container.messageDigest.digest());
     } finally {
       digestPool.release(container);
     }
}

锁的缓存做了2级,一级是通过上面的key和锁放在一个HashMap中;

//DiskCacheWriteLocker.class

private final Map<String, WriteLock> locks = new HashMap<>();
private final WriteLockPool writeLockPool = new WriteLockPool();

另一级是定义在内部类WriteLockPool的ArrayDeque里面,默认大小是10

 private static class WriteLockPool {
    private static final int MAX_POOL_SIZE = 10;
    private final Queue<WriteLock> pool = new ArrayDeque<>();
    ...
}

取的时候先从HashMap中取,取不到再从WriteLockPool中取

void acquire(String safeKey) {
    WriteLock writeLock;
    synchronized (this) {
      writeLock = locks.get(safeKey);
      if (writeLock == null) {
        writeLock = writeLockPool.obtain();
        locks.put(safeKey, writeLock);
      }
      writeLock.interestedThreads++;
    }

    writeLock.lock.lock();
}

释放锁的时候,会把writeLock的interestedThreads进行减一操作,如果为0了就释放锁,放入WriteLockPool中

void release(String safeKey) {
    WriteLock writeLock;
    synchronized (this) {
      writeLock = Preconditions.checkNotNull(locks.get(safeKey));
      if (writeLock.interestedThreads < 1) {
        ...
      }

      writeLock.interestedThreads--;
      if (writeLock.interestedThreads == 0) {
        WriteLock removed = locks.remove(safeKey);
        ...
        writeLockPool.offer(removed);
      }
    }

    writeLock.lock.unlock();
}

欢迎关注我的公众号AntDream查看更多精彩文章!

AntDream

标签:writeLock,面试题,缓存,Glide,--,private,Android,safeKey,final
From: https://blog.csdn.net/myth13141314/article/details/140090751

相关文章

  • android 代码如何增加atrace跟踪
    在Android代码中增加Atrace跟踪,可以使用Android提供的android.os.Trace类。这允许你在应用代码中手动添加自定义的跟踪点,以捕获特定代码段的执行情况。以下是如何在Android代码中增加Atrace跟踪的步骤:导入android.os.Trace:首先,在你的Java或Kotlin文件中导入......
  • 2025秋招计算机视觉面试题(七)-NMS详细工作机制及代码实现
    问题看到一句话:NMS都不懂,还做什么Detection!虎躯一震……懂是大概懂,但代码能写出来吗???在目标检测网络中,产生proposal后使用分类分支给出每个框的每类置信度,使用回归分支修正框的位置,最终会使用NMS方法去除同个类别当中IOU重叠度较高且scores即置信度较低的那些......
  • 【笔记】Android Settings 应用设置菜单的界面代码介绍
    简介Settings应用中,提供多类设置菜单入口,每个菜单内又有各模块功能的实现。那么各个模块基于Settings基础的界面Fragment去实现UI,层层按不同业务进行封装继承实现子类:DashboardFragmentSettingsPreferenceFragment功能设置页中的菜单又是通过Controller去实现业务并进行UI......
  • 大数据面试题之Flink(1)
    目录Flink架构 Flink的窗口了解哪些,都有什么区别,有哪几种?如何定义? Flink窗口函数,时间语义相关的问题 介绍下Flink的watermark(水位线),watermark需要实现哪个实现类,在何处定义?有什么作用? Flink的窗口(实现)机制 说下Flink的CEP 说一说Flink的Checkpoint机制 ......
  • 大数据面试题之Flink(2)
    Flink中Checkpoint超时原因 Flink的ExactlyOnce语义怎么保证? Flink的端到端ExactlyOnce Flink的水印(Watermark),有哪几种? Flink的时间语义 Flink相比于其它流式处理框架的优点? Flink和Spark的区别?什么情况下使用Flink?有什么优点? FlinkbackPressure反压机......
  • 大数据面试题之Flink(3)
    如何确定Flink任务的合理并行度? Flink任务如何实现端到端一致? Flink如何处理背(反)压? Flink解决数据延迟的问题 Flink消费kafka分区的数据时flink件务并行度之间的关系 使用flink-client消费kafka数据还是使用flink-connector消费 如何动态修改Flink的配置,前提......
  • Android系统签名简介
    apk的签名,简单说开发者可以通过签名对应用进行标识和更新。包名在一个设备上是唯一的,这样可以避免被相同包名应用随意覆盖安装。这是一个非常重要的安全功能。系统中的签名文件,也是对系统中应用进行签名,编译应用是可以指定签名类型。 下面介绍的是Android系统中的签名相关内容......
  • Android SurfaceFlinger——创建Surface(二十一)
           通过前面的篇文章我们简单了解了Surface和Layer,并且知道了SurfaceComposerClient的createSurface()方法最终创建的其实是一个Layer,这里我们来看一下真正的获取Surface的方法。一、获取Surface       通过系统动画的播放流程中我们知道真正......
  • Android super.img结构及解包和重新组包
    Androidsuper.img结构及解包和重新组包从Android10版本开始,Android系统使用动态分区,system、vendor、odm等都包含在super.img里面,编译后的最终镜像不再有这些单独的image,取而代之的是一个总的super.img.1.基础知识1.1为什么用super分区代替独立的分区?传统的分区方......
  • 2024 Redis面试题
    Redis为什么快?1.纯内存KV操作        Redis的操作都是基于内存的,CPU不是Redis性能瓶颈,,Redis的瓶颈是机器内存和网络带宽。        在计算机的世界中,CPU的速度是远大于内存的速度的,同时内存的速度也是远大于硬盘的速度。redis的操作都是基于内......