Glide GifDrawable加载流程

GifDrawable

一、整体流程概览

在Glide中播放GIF图像的核心流程如下:

  1. ​GIF资源识别​ :Glide通过GifResourceDecoder识别GIF文件格式
  2. ​资源解码​ :GifDrawable和GifFrameLoader负责GIF帧的解码管理
  3. ​帧调度​ :GifFrameLoader实现精准帧调度
  4. ​动画播放​ :GifDrawable将动画帧展示在View上
  5. ​资源回收​:结合Glide生命周期自动回收资源

二、核心源码分析

1. GifDrawable类结构

GifDrawable继承关系如下:

java 复制代码
public class GifDrawable 
    extends Drawable 
    implements Animatable, Animatable2Compat, GifFrameLoader.FrameCallback
  • 实现了Drawable:可以在任何支持Drawable的View上显示
  • Animatable接口:提供start()/stop()等动画控制方法
  • Animatable2Compat:增强的生命周期感知动画
  • FrameCallback:接收帧准备好的回调

2. GIF帧调度机制

核心类GifFrameLoader处理帧调度:

arduino 复制代码
public class GifFrameLoader {
    // 关键成员
    private final FrameCallback callback;  // GifDrawable实现的回调接口
    private final GifDecoder gifDecoder; // GIF解码器
    private final Handler handler;        // 主线程Handler
    private final BitmapPool bitmapPool;  // Glide的Bitmap池
    
    // 帧调度机制
    public void getNextFrame() {
        long targetTime = SystemClock.uptimeMillis() + currentFrame.getDelay();
        handler.postAtTime(new LoadNextFrame(), targetTime);
    }
}

3. GifDrawable与帧交互的关键方法

3.1. 帧准备回调

当帧准备好时,GifFrameLoader通过回调通知GifDrawable:

scss 复制代码
// GifDrawable.java
@Override
public void onFrameReady(int frameIndex) {
    // 1. 更新当前帧的Bitmap
    setCurrentFrame();
    
    // 2. 重绘自身触发View更新
    invalidateSelf();
    
    // 3. 调度下一帧
    frameLoader.getNextFrame();
}
3.2. 动画控制方法
scss 复制代码
// GifDrawable.java
@Override
public void start() {
    if (!isRunning) {
        // 注册动画生命周期
        registerAnimationCallback();
        // 开始第一帧调度
        frameLoader.getNextFrame();
        isRunning = true;
    }
}

@Override
public void stop() {
    if (isRunning) {
        // 停止所有帧调度
        frameLoader.clear();
        isRunning = false;
        // 取消注册
        unregisterAnimationCallback();
    }
}
3.3. 与View的渲染绑定
scss 复制代码
// GifDrawable.java
@Override
public void draw(Canvas canvas) {
    if (currentFrame != null) {
        // 绘制当前帧
        canvas.drawBitmap(currentFrame, srcRect, destRect, paint);
        
        // 检查是否需应用透明优化
        if (applyGravity) {
            calculateDestRect();
        }
    }
}

4. GIF解码流程

GifDecoder负责GIF帧数据的解码:

csharp 复制代码
public interface GifDecoder {
    // 获取下一帧图像
    Bitmap getNextFrame();
    
    // 获取帧延迟时间
    int getDelay();
    
    // 获取总帧数
    int getFrameCount();
}

实现类StandardGifDecoder的简化解码流程:

arduino 复制代码
// StandardGifDecoder.java
public Bitmap getNextFrame() {
    // 1. 读取下一帧数据
    // 2. 计算解码参数
    // 3. 使用BitmapPool获取Bitmap内存
    bitmap = bitmapPool.get(width, height, Bitmap.Config.ARGB_8888);
    // 4. 渲染到Bitmap
    destPixels = bitmap.getPixels();
    // ...渲染操作
    return bitmap;
}

5. 资源管理机制

5.1. 生命周期绑定

GifDrawable实现Animatable2Compat以便绑定到View的生命周期:

csharp 复制代码
// GifDrawable.java
private void registerAnimationCallback() {
    // 绑定到View
    if (this.callback != null && ViewCompat.isAttachedToWindow(view)) {
        animatable2Compat.registerAnimationCallback(callback);
    }
}
5.2. 资源回收

与Glide的生命周期自动回收系统集成:

scss 复制代码
// GifDrawable.java
@Override
public void recycle() {
    // 1. 清空当前帧
    currentFrame = null;
    
    // 2. 回收解码器资源
    frameLoader.clear();
    
    // 3. 释放位图引用
    frameLoader.onFrameCleared();
    
    // 4. 取消所有回调
    unregisterAnimationCallback();
}

三、Glide加载GIF的完整流程

  1. ​发起请求​ : Glide.with(context) .asGif() .load(url) .into(imageView);
  2. ​解码识别​ : - StreamGifDecoder识别输入流是否为GIF格式 - 创建GifResourceDecoder
  3. ​资源封装​ : - 创建GifDrawable实例 - 初始化GifFrameLoader - 创建GifDecoder(默认StandardGifDecoder)
  4. ​视图绑定​ : - 在GifDrawable目标View的onLoadStarted()中调用start()
less 复制代码
// GifDrawableImageViewTarget.java 
@Override protected void onResourceReady(@NonNull GifDrawable resource) {
// 开始动画播放     
resource.start(); 
} 
  1. ​帧调度流程​ : - GifFrameLoader请求第1帧 - GifDecoder解码帧 - 通过回调onFrameReady()更新Drawable - 触发视图重绘 - 调度下一帧
  2. ​生命周期结束​ : - Glide调用GifDrawable的clear() - 停止动画 - 回收所有帧资源 - 释放Bitmap内存

四、关键技术原理

1. 帧调度算法:精确帧间隔控制

关键代码实现:

java 复制代码
// GifFrameLoader.java
private class LoadNextFrame implements Runnable {
    @Override
    public void run() {
        // 1. 请求下一帧
        loadNextFrame();
        
        // 2. 计算下一帧时间点
        long nextFrameTime = currentFrame.getDelay() + SystemClock.uptimeMillis();
        
        // 3. 通过Handler精准调度
        handler.postAtTime(this, nextFrameTime);
    }
}

2. 内存优化策略

GifDrawable采用多层优化减少内存占用:

优化点 实现机制 效果
Bitmap池 通过BitmapPool复用Bitmap 减少对象创建/GC开销
帧复用 缓存常用帧 减少解码次数
惰性加载 按需解码帧 降低瞬时内存峰值
差分帧 仅解码变化部分 降低解码复杂度

3. 生命周期感知

集成Glide生命周期管理系统:

java 复制代码
// GifDrawable.java
public void setVisible(boolean visible, boolean restart) {
    super.setVisible(visible, restart);
    if (visible) {
        startIfNeeded();
    } else {
        stop();
    }
}

绑定到View生命周期:

java 复制代码
public void registerAnimationCallback(@NonNull AnimationCallback callback) {
    if (animatable2Compat != null) {
        animatable2Compat.registerAnimationCallback(callback);
    }
}

五、实际使用优化建议

1. 精准控制策略

java 复制代码
Glide.with(context)
    .asGif()
    .load(url)
    .listener(new RequestListener<GifDrawable>() {
        @Override
        public boolean onResourceReady(GifDrawable resource, ...) {
            // 1. 获取帧数
            int frameCount = resource.getFrameCount();
            
            // 2. 获取帧时长
            for (int i = 0; i < frameCount; i++) {
                int delay = resource.getFrameDuration(i);
                Log.d("GIF", "Frame $i delay: " + delay + "ms");
            }
            
            // 3. 开始播放
            resource.start();
            
            // 4. 监听循环结束
            resource.registerAnimationCallback(new AnimationCallback() {
                @Override
                public void onAnimationEnd(Drawable drawable) {
                    // 循环结束处理
                }
            });
            
            return false;
        }
    })
    .into(imageView);

2. 性能优化技巧

  1. ​减小GIF尺寸​:
java 复制代码
.override(300, 300) // 限制解码尺寸 
  1. ​控制播放次数​:
ini 复制代码
// 自定义Decoder设置循环次数 
GifDrawable drawable = resource.mutate(); drawable.setLoopCount(3); 
  1. ​内存监控​:
java 复制代码
// 监控内存占用 
Runtime runtime = Runtime.getRuntime(); 
long usedMemory = runtime.totalMemory() - runtime.freeMemory(); 
  1. ​暂停恢复策略​:
java 复制代码
// 根据App状态控制动画 
@Override protected void onPause() {
gifDrawable.stop(); 
}  
@Override protected void onResume() {     
if (!gifDrawable.isRunning()) { 
gifDrawable.start();     
} 
} 
相关推荐
海水冷却12 小时前
Android 群组聊天怎么实现:基于 ZIM 的完整落地指南(附可运行 Demo)
android
三少爷的鞋14 小时前
现代 Android 架构指南:不要再主动设计 Callback 接口
android
TimeFine20 小时前
AI 编码工作流的设计
android·ai编程
骑着蜗牛撵大象32721 小时前
C++20 协程 × 屏障:手写一个 co_await 版 Barrier,让等待不再钉死线程
android·c++20·协程·并发编程·co_await·屏障同步
山东孙燕姿1 天前
设备台账重构:从一张表到三层模型
android·java·网络·低代码·重构·rxjava
蒸鱼Yuzheng1 天前
移动端 CPU 调度分析:线程亲和性、大小核迁移与卡顿
android·perfetto·性能分析·线程优化·cpu调度
booboosui1 天前
安卓唤醒词:12 维 MFCC 加 DTW,阈值 1.8
android
传奇开心果1 天前
Compose Multiplatform 共享边界实战:架构契约、互操作同步与可回退设计
android·kotlin
事圆则缓1 天前
从开机到首帧:Android 系统与应用启动流程源码分析
android
AI吃大瓜1 天前
指针压力表读数4:Android实现指针压力表读数(可实时检测)
android·指针读数·指针压力表·压力表·压力表读数