问题
1 | Glide 中 LruPoolStrategy 是如何设计的 |
LruPoolStrategy 接口设计
1 | /** |
SizeConfigStrategy
SizeConfigStrategy
是一个实现了 LruPoolStrategy
接口的类,用于管理和重用 Bitmap
对象,其核心目标是通过控制 Bitmap
对象的尺寸和配置来优化内存使用。
这个策略通过将 Bitmap
对象分类存储在一个组织良好的结构中,并在需要时提供快速访问,以减少内存分配和回收的开销。下面是对这个类的关键部分的详细解析:
核心数据结构和方法
ARGB_8888_IN_CONFIGS
RGA_F16_IN_CONFIGS
RGB_565_IN_CONFIGS
ARGB_4444_IN_CONFIGS
ALPHA_8_IN_CONFIGS
这些数组定义了在不同 Android 版本下,根据请求的Bitmap.Config
所能接受的配置类型。例如,如果请求的是ARGB_8888
,那么可能接受的配置就包括了ARGB_8888
和RGBA_F16
(在支持的 Android 版本上)。keyPool:
KeyPool
实例,用于重用Key
对象。每个Key
对象代表一个Bitmap
的尺寸和配置,这样可以减少内存分配。groupedMap:
GroupedLinkedMap<Key?, Bitmap>
实例,用于根据Key
存储和检索Bitmap
对象。这种结构支持快速查找、插入和删除操作。sortedSizes:
NavigableMap<Int, Int>
的映射,用于跟踪每种配置下不同大小的Bitmap
数量。这对于找到最匹配的Bitmap
尺寸非常有用。
方法
put(bitmap: Bitmap):
将Bitmap
添加到池中。这个方法计算Bitmap
的字节大小,创建或获取一个对应的Key
,并更新groupedMap
和sortedSizes
。get(width: Int, height: Int, config: Bitmap.Config?):
尝试根据提供的宽度、高度和配置从池中获取一个最匹配的Bitmap
。这涉及到查找一个尺寸合适、配置兼容的Bitmap
,如果找到,就对其进行重新配置并返回。removeLast():
移除并返回池中最后一个Bitmap
,这通常是最近最少使用的一个。这个方法还会更新sortedSizes
以反映 变化。logBitmap(bitmap: Bitmap) 和 logBitmap(width: Int, height: Int, config: Bitmap.Config?):
生成表示Bitmap
尺寸和配置的字符串,用于日志记录和调试。
内部类
- KeyPool 和 Key:
这些类支持Bitmap
尺寸和配置的高效存储和检索。KeyPool
用于管理Key
对象的池,以减少创建新对象的需要。Key
对象表示一个Bitmap
的尺寸和配置,用作groupedMap
中的键。
整体设计思路
SizeConfigStrategy
的设计旨在通过细致管理 Bitmap
对象的存储和重用来优化内存使用。它通过精确匹配请求的 Bitmap
尺寸和配置,尽量减少创建新 Bitmap
对象的需要,从而降低了内存压力和提高了性能。这个策略特别适用于图片密集型的应用,比如图片浏览器或社交媒体应用,其中频繁地加载和显示图片。
AttributeStrategy
这段代码定义了一个名为 AttributeStrategy
的内部类,实现了 LruPoolStrategy
接口,用于管理位图(Bitmap)的缓存策略。这个策略通过位图的宽度、高度和配置(Bitmap.Config)来唯一标识和管理位图。下面是对这个类的主要组成部分和逻辑的解析:
类的主要组成部分
KeyPool 类:一个用于管理
Key
对象池的内部类。它通过重写create()
方法来创建新的Key
对象,并提供了一个获取Key
的方法,该方法接受位图的宽度、高度和配置作为参数,用于初始化Key
。Key 类:一个内部类,实现了
Poolable
接口。每个Key
对象包含位图的宽度、高度和配置属性。Key
类提供了init
方法来设置这些属性,equals
和hashCode
方法被重写以确保Key
对象可以根据其宽度、高度和配置被唯一地标识和比较。groupedMap:一个
GroupedLinkedMap
对象,用于根据Key
(位图的宽度、高度和配置)分组存储和管理位图。
类的主要方法
put(bitmap: Bitmap)
:将位图添加到缓存中。首先通过keyPool
获取与位图尺寸和配置对应的Key
,然后将位图和Key
添加到groupedMap
中。get(width: Int, height: Int, config: Bitmap.Config?)
:尝试获取一个符合指定尺寸和配置的位图。首先通过keyPool
获取与指定尺寸和配置对应的Key
,然后从groupedMap
中查找和返回相应的位图。removeLast()
:移除并返回最近最少使用的位图。这是通过从groupedMap
中移除最后一个位图来实现的。logBitmap(bitmap: Bitmap)
和logBitmap(width: Int, height: Int, config: Bitmap.Config?)
:用于生成表示位图尺寸和配置的日志字符串。getSize(bitmap: Bitmap)
:返回位图占用的字节大小。
特点和用途
AttributeStrategy
通过精确地考虑位图的尺寸和配置来管理位图缓存,使其能够更有效地利用内存并提高缓存的效率。通过使用对象池来管理 Key
对象,还可以减少内存分配和垃圾回收的压力。这种策略特别适用于需要存储和管理多种尺寸和配置位图的应用场景。
SizeStrategy
这段代码是一个用于管理位图(Bitmap)缓存策略的内部类 SizeStrategy
,它实现了 LruPoolStrategy
接口。这个策略的核心是通过位图大小来管理和回收位图资源,以优化内存使用。下面是对这个类和它的主要组成部分的分析:
成员变量介绍:
keyPool
: 一个KeyPool
对象,用于管理Key
对象的池。每个Key
对象都与一个特定大小的位图相关联。groupedMap
: 一个GroupedLinkedMap<Key, Bitmap>
对象,用于根据Key
分组存储Bitmap
对象。这允许快速查找和回收特定大小的位图。sortedSizes
: 一个NavigableMap<Int?, Int>
对象,存储每个大小的位图数量。这是一个PrettyPrintTreeMap
,可能是为了便于调试和打印。方法解析:
put(bitmap: Bitmap)
: 将一个位图添加到缓存中。它计算位图的大小,获取或创建相应大小的Key
,将位图和Key
添加到groupedMap
中,并更新sortedSizes
中对应大小的计数。get(width: Int, height: Int, config: Bitmap.Config?)
: 尝试获取一个符合指定宽度、高度和配置的位图。它计算所需位图的大小,查找是否有足够大的可用位图,如果有,则从groupedMap
中取出并返回该位图。removeLast()
: 移除并返回最近最少使用(LRU)的位图。这是通过从groupedMap
中移除最后一个位图来实现的,并更新sortedSizes
中的计数。decrementBitmapOfSize(size: Int?)
: 减少特定大小的位图数量。如果该大小的位图只有一个,则从sortedSizes
中移除该大小;否则,减少其计数。辅助类:
KeyPool
: 用于管理Key
对象池的类。它重写了create()
方法来生成新的Key
对象,并提供了一个重载的get(size: Int)
方法来获取或创建一个初始化了特定大小的Key
。Key
: 实现了Poolable
接口的类,表示与位图大小相关联的键。包含一个size
属性和init(size: Int)
方法来设置键的大小。重写了equals()
和hashCode()
方法以支持正确的键比较和哈希操作。
常量和辅助方法:
MAX_SIZE_MULTIPLE
: 一个常量,定义了在查找时可以接受的最大位图大小倍数。getBitmapString(bitmap: Bitmap)
和getBitmapString(size: Int)
: 辅助方法,用于生成表示位图大小的字符串。
这个类的设计目的是提高位图缓存的效率和灵活性,通过精细地管理不同大小的位图来优化内存使用。通过维护一个有序的大小映射和一个根据大小分组的位图映射,它可以快速地存取和回收位图资源。
SizeConfigStrategy
、AttributeStrategy
、和SizeStrategy
是Glide图像加载库用于位图缓存管理的三种不同策略,它们在位图的存储、查找和回收方式上各有特点。这些策略优化了内存使用,并改善了图像加载的性能。以下是它们的区别和各自适用的场景:
适用场景
SizeConfigStrategy
特点:SizeConfigStrategy
使用位图的大小(以字节为单位)和Bitmap.Config
配置作为键来管理缓存。这种方法允许区分具有相同像素大小但不同像素配置的位图,如ARGB_8888
和RGB_565
。
适用场景:这种策略适用于需要根据位图的内存大小和配置精细管理缓存的应用。例如,如果应用中同时使用了不同配置的位图(以优化显示质量和内存使用),SizeConfigStrategy
能有效地区分和管理这些位图。
AttributeStrategy
特点:AttributeStrategy
基于位图的宽度、高度和配置(Bitmap.Config
)来识别和管理位图。这种方法提供了对缓存的精确控制,允许缓存系统区分尺寸相同但配置不同的位图。
适用场景:当应用需要在相同的尺寸下缓存不同配置的位图,且这些配置对位图的使用和性能有明显影响时,AttributeStrategy
非常适用。它确保了即使是细微的配置差异也能被正确管理,适合对图像质量和性能有高要求的应用。
SizeStrategy
特点:SizeStrategy
仅基于位图占用的内存大小来管理缓存,不考虑位图的配置或尺寸。这种策略通过一种更简单的方式来回收和重用位图内存,忽略了位图的其他属性。
适用场景:对于那些不需要考虑位图配置差异,主要关注于减少内存占用和简化缓存管理的应用,SizeStrategy
是一个理想的选择。它适合内存使用更为紧张,或者位图配置较为统一的场景。
总结
SizeConfigStrategy
和 AttributeStrategy
提供了更细粒度的缓存管理,能够根据位图的具体特征(如配置和尺寸)进行优化,适合需要高度优化内存使用和图像质量的场景。
SizeStrategy
通过一个更简单的方法来管理缓存,适用于对缓存管理的要求相对简单,更关注于减少内存占用的应用。
选择哪种策略取决于应用的具体需求,包括对内存管理的敏感度、图像的多样性以及性能的要求。
三种池子的特点
SizeConfigStrategy
- 内存大小:使用位图的内存大小作为缓存的关键因素之一。
- 配置敏感:考虑了
Bitmap.Config
,区分了相同大小但配置不同的位图。 - 精细管理:允许对缓存的位图进行更精细的管理,适用于内存和显示质量都很重要的场景。
AttributeStrategy
- 尺寸配置:基于位图的宽度、高度和
Bitmap.Config
来管理位图。 - 高度区分:能够精确区分尺寸相同但配置不同的位图。
- 细节控制:提供对位图缓存的细节控制,适用于对图像显示细节有高要求的应用。
SizeStrategy
- 简化内存:仅基于位图占用的内存大小来管理缓存,简化了缓存管理。
- 统一处理:不区分位图的尺寸或配置,统一处理所有位图。
- 内存优化:优先考虑内存使用效率,适用于内存敏感且配置统一的应用场景。
这些关键字概括了每种策略的核心特点和适用场景,有助于在实际开发中根据应用的需求选择最合适的位图缓存管理策略。
附件
SizeConfigStrategy
1 | class SizeConfigStrategy : LruPoolStrategy { |
AttributeStategy
1 | package com.max.hbbitmappool.pool.impl |
SizeStrategy
1 | internal class SizeStrategy : LruPoolStrategy { |
三种池子精细化管理排序
- AttributeStrategy
- SizeConfigStrategy
- SizeStrategy
1. AttributeStrategy
- 精细化等级:最高
- 原因:
AttributeStrategy
基于位图的宽度、高度和Bitmap.Config
配置来管理位图,提供了最细致的控制。这允许它区分具有相同像素数量但不同尺寸或配置的位图,实现了对位图缓存的高度精细化管理。
2. SizeConfigStrategy
- 精细化等级:中等
- 原因:
SizeConfigStrategy
结合了位图的内存大小和配置(如ARGB_8888
、RGB_565
等)来管理位图。虽然它不如AttributeStrategy
能够精确到位图的具体尺寸,但通过考虑配置信息,它在位图的管理上提供了比仅基于大小更精细的控制。
3. SizeStrategy
- 精细化等级:最低
- 原因:
SizeStrategy
仅基于位图占用的内存大小来管理位图,完全忽略了位图的尺寸和配置信息。这种策略提供了最简单的管理方式,适合于那些内存使用效率是主要关注点、对位图的具体属性(如尺寸和配置)关注较少的场景。
总结来说,如果需要对缓存中的位图进行非常精细化的管理,优先选择AttributeStrategy
;
如果希望在精细化管理和简化逻辑之间取得平衡,SizeConfigStrategy
是一个好的选择;
而如果主要关注简化缓存管理和优化内存使用,SizeStrategy
将是最合适的策略。