目录
  1. 1. 一、图片加载框架的核心问题
  2. 2. 二、Glide 的请求处理流水线
    1. 2.1. 2.1 Glide 的注册表机制(Registry)
    2. 2.2. 2.2 ModelLoader 的工作机制
    3. 2.3. 2.3 Engine:缓存策略的核心
    4. 2.4. 2.4 三层缓存架构
  3. 3. 三、Bitmap 复用池:LruBitmapPool
  4. 4. 四、图片采样的 Downsampler
  5. 5. 五、生命周期绑定:RequestManagerFragment
    1. 5.1. 5.1 在 Fragment 中的生命周期绑定
  6. 6. 六、变换(Transformation)与缓存键
  7. 7. 七、Coil:Kotlin 协程的轻量级方案
    1. 7.1. 7.1 Coil vs Glide vs Fresco 综合对比
    2. 7.2. 7.2 Fresco 的独特设计
  8. 8. 八、面试常问题目
解读开源框架系列-图片加载框架设计

一、图片加载框架的核心问题

在 Android 上加载和显示图片,看似简单实则包含了一系列棘手的问题:

  1. 内存管理:Bitmap 是 Android 中最占内存的对象。一张 1024x1024 的 ARGB_8888 图片占用 4MB,而屏幕通常只有 1080px 宽。如果不缩放,列表滑动时会迅速耗尽内存。
  2. 图片复用:创建和回收 Bitmap 的代价很高,需要池化复用——Bitmap 的像素数据存储在 native 堆中,Java 堆中的 Bitmap 对象只是一个壳。
  3. 生命周期绑定:Activity 销毁后,未完成的图片请求应被取消,否则会导致内存泄漏和崩溃。
  4. 缓存策略:既要内存缓存(快速访问),又要磁盘缓存(持久化),还要在两者之间找到平衡。
  5. 格式适配:不同的图片格式(PNG、JPEG、WebP、GIF、SVG、HEIF)需要不同的解码器。
  6. 变换处理:图片加载后可能需要进行裁剪、圆角、模糊等变换。
  7. 渐进式加载:大图应先显示低分辨率预览,再逐步加载高分辨率版本。

Glide(https://github.com/bumptech/glide)是 Google 推荐的图片加载框架,被广泛部署在 Android 应用中。Coil(https://github.com/coil-kt/coil)是 Kotlin 生态的后起之秀。Fresco(https://github.com/facebook/fresco)专注于内存管理优化。

二、Glide 的请求处理流水线

Glide 的图片加载流水线是一套分层解耦的流水线,每个节点有明确的职责:

RequestManager (生命周期管理)

RequestBuilder (配置请求参数:URL、大小、变换、占位图等)

Request (代表一次加载任务:SingleRequest / ThumbnailRequestCoordination)

Engine (加载引擎,负责缓存策略,协调内存缓存/磁盘缓存/网络请求)

DecodeJob (解码任务,实现 Runnable,提交到线程池执行)

┌────────────────────────────────────────────────────┐
│ DecodeJob.run() → runGenerators() │
│ 1. ResourceCacheGenerator (资源缓存) │
│ 2. DataCacheGenerator (数据缓存) │
│ 3. SourceGenerator (从源加载:网络/文件) │
└────────────────────────────────────────────────────┘

ModelLoader (数据加载:HttpUrlFetcher, FileFetcher 等)

DecodePath (解码:将 InputStream 解码为 Bitmap/Drawable)

Transformation (变换:CenterCrop, CircleCrop, RoundedCorners 等)

Target → ImageView (最终显示)

2.1 Glide 的注册表机制(Registry)

Glide 使用注册表模式来管理各种类型的处理组件。核心在 com.bumptech.glide.Registry 类中:

// Glide.java 构造函数中的 Registry 构建
registry
// 注册 ModelLoader:如何根据 Model 类型获取数据
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
.append(File.class, InputStream.class, new FileLoader.StreamFactory())
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
// 注册 ResourceDecoder:如何将数据解码为特定类型
.append(InputStream.class, Bitmap.class, new StreamBitmapDecoder(downsampler, ...))
.append(ByteBuffer.class, Bitmap.class, new ByteBufferBitmapDecoder(downsampler))
// GIF 解码
.append(InputStream.class, GifDrawable.class,
new StreamGifDecoder(registry.getImageHeaderParsers(), byteBufferGifDecoder, ...))
// WebP 解码(通过 BitmapFactory,需 Android 4.0+)
// 注册 Encoder:如何将数据写入磁盘缓存
.append(Bitmap.class, new BitmapEncoder())
.append(GifDrawable.class, new GifDrawableEncoder())
// 注册 ResourceTranscoder:如何在不同资源类型间转换
.register(GifDrawable.class, Bitmap.class, new GifBitmapProvider(bitmapPool));

Registry 允许开发者注册自定义的 ModelLoader(如从加密文件加载)和 ResourceDecoder(如支持 HEIF 格式)。这是 Glide 可扩展性的核心。

2.2 ModelLoader 的工作机制

ModelLoader 负责将”模型”(如 URL、File、Uri)转换为”数据”(InputStream、ByteBuffer、ParcelFileDescriptor):

// ModelLoader 接口定义
public interface ModelLoader<Model, Data> {
LoadData<Data> buildLoadData(@NonNull Model model, int width, int height,
@NonNull Options options);

boolean handles(@NonNull Model model);

class LoadData<Data> {
public final Key sourceKey;
public final List<Key> alternateKeys;
public final DataFetcher<Data> fetcher;
}
}

// DataFetcher 负责实际的数据读取
public interface DataFetcher<T> {
void loadData(@NonNull Priority priority, @NonNull DataCallback<? super T> callback);
void cleanup();
void cancel();
Class<T> getDataClass();
DataSource getDataSource();
}

每个 ModelLoader 返回一个 LoadData,其中包含 DataFetcher。Glide 通过 model 类型(如 GlideUrl)和期望的 data 类型(如 InputStream)来查找匹配的 ModelLoader。

2.3 Engine:缓存策略的核心

// com.bumptech.glide.load.engine.Engine(简化)
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {

private final MemoryCache cache; // LruResourceCache(内存缓存)
private final Map<Key, WeakReference<EngineResource<?>>> activeResources; // 活跃资源
private final EngineJobFactory engineJobFactory;
private final DecodeJobFactory decodeJobFactory;

public <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width, int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb, Executor callbackExecutor) {

EngineKey key = keyFactory.buildKey(model, signature, width, height,
transformations, resourceClass, transcodeClass, options);

// 1. 从活跃资源中查找(弱引用 Map,持有正在使用的资源)
EngineResource<?> memoryResource = loadFromActiveResources(key, isMemoryCacheable);
if (memoryResource != null) {
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}

// 2. 从内存缓存中查找(LRU 缓存)
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
return null;
}

// 3. 检查是否已有相同 key 的 job 正在执行(去重)
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
return new LoadStatus(cb, current);
}

// 4. 内存缓存未命中 → 启动 DecodeJob 从磁盘或网络加载
EngineJob<R> engineJob = engineJobFactory.build(
key, isMemoryCacheable, useUnlimitedSourceExecutorPool,
useAnimationPool, onlyRetrieveFromCache);

DecodeJob<R> decodeJob = decodeJobFactory.build(
glideContext, model, key, signature, width, height,
resourceClass, transcodeClass, priority, diskCacheStrategy,
transformations, isTransformationRequired, isScaleOnlyOrNoTransform,
options, engineJob);

jobs.put(key, engineJob);
engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob);

return new LoadStatus(cb, engineJob);
}
}

Engine 的 jobs Map 实现了 加载去重:当多个 ImageView 同时请求同一张图片时,只有一个 DecodeJob 真正执行加载,其他请求共享结果。这通过 EngineJob.addCallback() 实现——同一个 job 可以有多个 callback。

2.4 三层缓存架构

Glide 的内存缓存分为两层:

第一层:活跃资源(Active Resources)——Map<Key, WeakReference<EngineResource<?>>>

这是当前正在被显示的图片。EngineResource 实现了引用计数。当 ImageView 开始显示图片时,计数 +1;ImageView 停止显示时,计数 -1。计数归零时,资源从 activeResources 中移除,降级进入 memoryCache。

引用计数的核心实现:

// EngineResource.java
class EngineResource<Z> implements Resource<Z> {
private int acquired;

synchronized void acquire() {
if (isRecycled) {
throw new IllegalStateException("Cannot acquire a recycled resource");
}
++acquired;
}

synchronized void release() {
if (acquired <= 0) {
throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
}
if (--acquired == 0) {
listener.onResourceReleased(key, this); // 通知 Engine 将资源放回 cache
}
}
}

第二层:LruResourceCache——基于 LRU 算法的内存缓存。

// com.bumptech.glide.load.engine.cache.LruResourceCache
public class LruResourceCache extends LruCache<Key, EngineResource<?>>
implements MemoryCache {
// 最大容量 = Application 可用内存的某个比例(默认 ~ 1/8)

@Override
protected int getSize(Key key, EngineResource<?> item) {
// 返回资源占用的字节数
return item.getSize();
}

@Override
protected void onItemEvicted(Key key, EngineResource<?> item) {
// 被 LRU 淘汰时,回收资源
item.recycle();
}
}

第三层:磁盘缓存(Disk Cache)——DiskLruCache(基于 Jake Wharton 的 DiskLruCache 实现)。

// com.bumptech.glide.load.engine.DiskCacheStrategy 枚举
public enum DiskCacheStrategy {
ALL, // 缓存原始数据和变换后的资源
NONE, // 不缓存磁盘
DATA, // 只缓存原始数据(下载后的未解码数据)
RESOURCE, // 只缓存变换后的资源
AUTOMATIC, // 智能决策(通过 DataFetcher.getDataClass() 判断)
}

缓存命中流程:

请求加载图片
→ 查 activeResources(弱引用 Map) → 命中则返回
→ 查 memoryCache(LruResourceCache) → 命中则返回,进入 activeResources
→ 查 diskCache(ResourceCacheGenerator) → 命中则解码,进入 memoryCache + activeResources
→ 查 diskCache(DataCacheGenerator) → 命中则解码+变换,进入 memoryCache + activeResources
→ 从 Source 加载(网络/文件) → 解码+变换,写入 diskCache + memoryCache + activeResources

为什么区分 activeResources 和 memoryCache?

这是一种分层淘汰策略。activeResources 中的资源正在被显示,不能被 LRU 策略回收——这可能导致正在显示的图片突然消失。LRU 池只管理”当前未使用但可能被再次使用”的资源。当不再显示时,引用计数归零,资源从 activeResources 降级到 memoryCache,等待 LRU 策略管理。

三、Bitmap 复用池:LruBitmapPool

Glide 的 Bitmap 复用池是一个独立的 LRU 池,专门管理可复用的 Bitmap 内存:

// com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool
public class LruBitmapPool implements BitmapPool {
private final LruPoolStrategy strategy; // SizeConfigStrategy(Android 4.4+)/ AttributeStrategy(兼容旧版)

@Override
public Bitmap get(int width, int height, Bitmap.Config config) {
Bitmap result = getDirtyOrNull(width, height, config);
if (result != null) {
// 复用 Bitmap:将其所有像素设为黑色/透明
result.eraseColor(Color.TRANSPARENT);
} else {
// 池中没有可复用的,创建新 Bitmap
result = Bitmap.createBitmap(width, height, config);
}
return result;
}

@Override
public void put(Bitmap bitmap) {
if (!bitmap.isMutable() || strategy.getSize(bitmap) > maxSize) {
bitmap.recycle(); // 不可复用或太大,直接回收
return;
}
strategy.put(bitmap);
}
}

SizeConfigStrategy 按 Size(宽每像素字节数)和 Config(ARGB_8888/RGB_565 等)将 Bitmap 分组。当请求一个 Bitmap 时,会在同 Size 级别的池中查找。

SizeConfigStrategy 的分组逻辑

// SizeConfigStrategy.java
static final class Key implements Poolable {
int width;
int height;
Bitmap.Config config;

// 注意:equals/hashCode 基于 size 和 config,而非精确的 width/height
// 这样宽高近似但 size 相同的 Bitmap 可以复用
}

DecodeJob 中,Downsampler 解码图片时会尝试从 BitmapPool 获取可复用的 Bitmap:

// Downsampler.java 核心逻辑
Bitmap result = bitmapPool.getDirtyOrNull(outWidth, outHeight, expectedConfig);
if (result == null) {
result = Bitmap.createBitmap(outWidth, outHeight, expectedConfig);
}
// 使用 inBitmap 复用
options.inBitmap = result;
bitmap = BitmapFactory.decodeStream(is, null, options);

inBitmap 的兼容性限制

  • Android 4.4 (API 19) 之前:inBitmap 只能复用尺寸完全相同的 Bitmap
  • Android 4.4+:inBitmap 可以复用任何尺寸不大于目标图片的 Bitmap(条件是 inSampleSize == 1,或者配置为 RGB_565 时可以复用任意配置)

四、图片采样的 Downsampler

图片通常比 ImageView 大得多,需要采样缩小以节省内存。Glide 的 Downsampler 负责这项工作:

// com.bumptech.glide.load.resource.bitmap.Downsampler.java
public Resource<Bitmap> decode(InputStream is, int requestedWidth,
int requestedHeight, Options options, ...) throws IOException {

// 1. 读取图片头部信息(不加载整个图片,使用 inJustDecodeBounds)
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, options);
int sourceWidth = options.outWidth;
int sourceHeight = options.outHeight;

// 2. 计算采样率
int sampleSize = getRoundedSampleSize(
calculateScaling(sourceWidth, sourceHeight, requestedWidth, requestedHeight));
options.inSampleSize = sampleSize;

// 3. 实际解码
options.inJustDecodeBounds = false;
// 4. 从 BitmapPool 获取可复用 Bitmap,设置 inBitmap
Bitmap result = bitmapPool.get(width, height, config);
options.inBitmap = result;

return BitmapResource.obtain(
BitmapFactory.decodeStream(is, null, options), bitmapPool);
}

inSampleSize 必须是 2 的幂次(1, 2, 4, 8…),Android 的解码器利用这个信息只解码排列中的某些像素点。

DownsampleStrategy 定义了四种采样策略:

// DownsampleStrategy 枚举
FIT_CENTER // 图片完整显示在 View 内(可能留白)
CENTER_INSIDE // 图片完整显示(不大于 View 且不小于原图)
CENTER_OUTSIDE // 图片填满 View(可能裁剪)
NONE // 不缩放,使用原图尺寸

对应不同的采样率计算逻辑。例如 CENTER_OUTSIDE 的采样率计算:

// CenterOutside.calculateScaling
float widthPercentage = requestedWidth / (float) sourceWidth;
float heightPercentage = requestedHeight / (float) sourceHeight;
// 取较大的缩放比例(确保至少一个维度填满)
float scaleFactor = Math.max(widthPercentage, heightPercentage);

五、生命周期绑定:RequestManagerFragment

Glide 通过注入一个无 UI 的 Fragment 来监听 Activity/Fragment 的生命周期:

// com.bumptech.glide.manager.RequestManagerRetriever.java
public RequestManager get(Activity activity) {
// 1. 如果 activity 在后台线程,使用 Application 的 RequestManager
android.app.FragmentManager fm = activity.getFragmentManager();
// 2. 查找现有的 SupportRequestManagerFragment
RequestManagerFragment current = getRequestManagerFragment(fm, null);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(activity.getApplicationContext());
requestManager = factory.build(
glide, current.getLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}

// RequestManagerFragment.java
public class RequestManagerFragment extends Fragment {
private final ActivityFragmentLifecycle lifecycle;

@Override
public void onStart() { super.onStart(); lifecycle.onStart(); }
@Override
public void onStop() { super.onStop(); lifecycle.onStop(); }
@Override
public void onDestroy() { super.onDestroy(); lifecycle.onDestroy(); }
}

当 Fragment 收到 onStop() 时,RequestManager 会调用 pauseRequests() 暂停图片加载(节省带宽);onDestroy() 时调用 clearRequests() 取消所有进行中和待处理的请求。这个设计使得开发者无需手动管理图片请求的生命周期。

5.1 在 Fragment 中的生命周期绑定

对于 Fragment 内部的 Glide 请求,Glide 会查找嵌套的 SupportRequestManagerFragment

// RequestManagerRetriever.java
public RequestManager get(Fragment fragment) {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getContext(), fm, null,
isActivityVisible(fragment.getActivity()));
}

确保子 Fragment 中的请求跟随 Fragment 的视图生命周期(在 onDestroyView 时 clear)。

六、变换(Transformation)与缓存键

Glide 的变换会生成新的缓存键,这意味着 url + centerCropurl + circleCrop 是两个不同的缓存条目:

// BaseRequestOptions.java
public T transform(@NonNull Transformation<Bitmap> transformation) {
transformations.put(Bitmap.class, transformation);
// 每个变换会影响缓存键
return self();
}

变换的实现基于 BitmapTransformation 抽象类:

public abstract class BitmapTransformation implements Transformation<Bitmap> {
@Override
public final Resource<Bitmap> transform(
@NonNull Context context, @NonNull Resource<Bitmap> resource,
int outWidth, int outHeight) {
BitmapPool bitmapPool = Glide.get(context).getBitmapPool();
Bitmap toTransform = resource.get();
Bitmap outBitmap = transform(context.getApplicationContext(), bitmapPool,
toTransform, outWidth, outHeight);

if (!toTransform.equals(outBitmap)) {
// 如果不能就地变换,释放原图
resource.recycle();
}

return BitmapResource.obtain(outBitmap, bitmapPool);
}

protected abstract Bitmap transform(
@NonNull BitmapPool pool, @NonNull Bitmap toTransform,
int outWidth, int outHeight);
}

CenterCrop 的实现

public class CenterCrop extends BitmapTransformation {
@Override
protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform,
int outWidth, int outHeight) {
// 计算缩放后的宽高(填满目标区域)
float scale = Math.max(
(float) outWidth / toTransform.getWidth(),
(float) outHeight / toTransform.getHeight());
int scaledWidth = Math.round(scale * toTransform.getWidth());
int scaledHeight = Math.round(scale * toTransform.getHeight());

// 创建 canvas,居中绘制(裁剪超出部分)
Bitmap result = pool.get(outWidth, outHeight, toTransform.getConfig());
Canvas canvas = new Canvas(result);
canvas.drawBitmap(toTransform, null,
new RectF((outWidth - scaledWidth) / 2f, ...), null);
return result;
}
}

七、Coil:Kotlin 协程的轻量级方案

Coil 是 Kotlin 生态的图片加载框架,架构上与 Glide 类似但更轻量(约 2000 个方法 vs Glide 的约 5000 个方法),完全基于 Kotlin 协程:

// Coil 的 ImageLoader 执行引擎
// coil-base/src/main/java/coil/RealImageLoader.kt
suspend fun execute(request: ImageRequest): ImageResult {
// 1. 检查内存缓存
val memoryCacheKey = request.memoryCacheKey
if (memoryCacheKey != null) {
val cached = memoryCacheService[memoryCacheKey]
if (cached != null) return cached
}

// 2. 创建 fetcher(根据 data 类型:HttpFetcher、FileFetcher、AssetFetcher 等)
val fetcher = registry.getFetcher(request.data, request.options)

// 3. 使用 kotlinx.coroutines 协程执行加载、解码、变换
val fetched = fetcher.fetch(request)
val decoded = registry.decode(fetched, request.options)
val transformed = registry.transform(decoded, request)

// 4. 缓存结果
memoryCacheService[memoryCacheKey] = transformed
return transformed
}

7.1 Coil vs Glide vs Fresco 综合对比

特性 Glide Coil Fresco
编程语言 Java / Kotlin Kotlin Java
异步方式 线程池 (ExecutorService) 协程 (Coroutines) 自定义线程池
方法数 ~5000 ~2000 ~7000
APK 增量 ~500KB ~200KB ~2MB
内存策略 BitmapPool + LRU 缓存 BitmapPool + LRU 缓存 Native 堆 + 自定义内存区域
GIF 支持 内置 需扩展 (coil-gif) 内置(WebP 动画也支持)
Compose 支持 accompanist-glide 原生 Coil (AsyncImage) 无官方 Compose 支持
生命周期集成 RequestManagerFragment 与 Lifecycle 协程天然集成 DraweeController
定制性 高(Registry 机制) 低(架构较封闭)
网络库耦合 需手动集成 OkHttp/Volley 默认 OkHttp 自带网络栈

Coil vs Glide 的主要差异:

  • Coil 使用 Kotlin 协程替代线程池,代码更简洁。
  • Coil 默认与 Lifecycle 集成,自动取消协程,无需额外配置。
  • Coil 支持 Compose(AsyncImage 组件),与 Jetpack Compose 无缝集成。
  • Coil 的 APK 体积更小(~200KB vs Glide 的 ~500KB+,不含 OkHttp)。
  • Glide 的 Registry 可扩展性更强——可以注册自定义的 ModelLoader 和 Decoder,支持更广泛的数据源和图片格式。

7.2 Fresco 的独特设计

Fresco 将 Bitmap 的像素数据存储在 ashmem(Android Shared Memory) 或 native heap 中,绕过 Java heap 的限制。在 Android 4.x 及更早版本中,这能有效避免 OOM。但随着 ART 的改进(Android 8+ Bitmap 像素数据默认存储在 native heap),Fresco 这个优势被大幅削弱。

八、面试常问题目

Q1: Glide 的三层缓存架构是如何工作的?为什么要区分 activeResources 和 memoryCache?

三层缓存:activeResources(弱引用 Map,正在使用的资源)→ LruResourceCache(内存 LRU 缓存)→ DiskCache(磁盘缓存)。区分 activeResources 和 memoryCache 的原因:(1) activeResources 中的资源正在被使用,不能被 LRU 策略回收;(2) 使用弱引用+引用计数,计数归零时资源自动从 activeResources 降级到 memoryCache;(3) LRU 池只管理”当前未使用但可能被再次使用”的资源。这是一种分层淘汰策略——正在用的永远留在内存,最近用过的次之,最老的被淘汰。

Q2: Glide 如何实现 Bitmap 的复用?

通过 LruBitmapPool 管理可复用的 Bitmap 对象。当新的 Bitmap 需要创建时,先从池中查找尺寸和配置匹配的 Bitmap(调用 bitmapPool.get()),如果找到则复用其内存(通过 BitmapFactory.Options.inBitmap 参数),只需在复用前擦除原有像素数据。当 Bitmap 不再使用时,调用 bitmapPool.put(bitmap) 放回池中。这避免了频繁的 malloc/native memory allocation 和 GC 压力。

Q3: Glide 的 RequestManagerFragment 是如何保证生命周期安全的?

Glide 通过 FragmentManager 注入一个无 UI 的 Fragment(RequestManagerFragment),利用 Fragment 的生命周期回调(onStart、onStop、onDestroy)来管理图片请求。当 Fragment 的 onStop 触发时,RequestManager 暂停该页面的图片请求(节省带宽);onDestroy 触发时,取消所有请求并释放资源。这个机制完全基于 Android Framework 的 Fragment 生命周期,无需依赖 androidx.lifecycle 包。

Q4: 为什么图片加载框架需要自定义 Downsampler 而不是直接用 BitmapFactory?

BitmapFactory 的原生采样(inSampleSize)只支持 2 的幂次采样(1, 2, 4, 8…),无法精确匹配 View 的尺寸。例如 ImageView 是 300dp(约 900px),图片是 4000x3000,最优的 inSampleSize 应该是 4(缩小到 1000px),剩余的缩放由变换(Transformation)完成。精确采样需要结合 inSampleSize 基数和后续的矩阵缩放(Matrix),这正是 Glide 的 Downsampler 所实现的。

Q5: Glide 的 Registry 机制是如何实现可扩展性的?

Registry 通过四个核心接口实现可扩展性:ModelLoader(模型→数据)、ResourceDecoder(数据→资源)、ResourceEncoder(资源→磁盘缓存)、ResourceTranscoder(资源类型转换)。开发者可以注册自定义实现来支持新的数据源(如加密文件、自定义 URI scheme)和新的图片格式(如 HEIF、AVIF)。每个组件有对应的 Factory 接口,Glide 通过运行时注册表查找匹配的处理链。


参考源码路径:

  • Glide 仓库:https://github.com/bumptech/glide
  • Engine:library/src/main/java/com/bumptech/glide/load/engine/Engine.java
  • DecodeJob:library/src/main/java/com/bumptech/glide/load/engine/DecodeJob.java
  • LruResourceCache:library/src/main/java/com/bumptech/glide/load/engine/cache/LruResourceCache.java
  • LruBitmapPool:library/src/main/java/com/bumptech/glide/load/engine/bitmap_recycle/LruBitmapPool.java
  • Downsampler:library/src/main/java/com/bumptech/glide/load/resource/bitmap/Downsampler.java
  • RequestManagerRetriever:library/src/main/java/com/bumptech/glide/manager/RequestManagerRetriever.java
  • Registry:library/src/main/java/com/bumptech/glide/Registry.java
  • ModelLoader:library/src/main/java/com/bumptech/glide/load/model/ModelLoader.java
  • Coil 仓库:https://github.com/coil-kt/coil
  • Fresco 仓库:https://github.com/facebook/fresco
打赏
  • 微信
  • 支付宝

评论