Glide GifDrawable加载流程

GifDrawable

一、整体流程概览

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

  1. ​GIF资源识别​ :Glide通过GifResourceDecoder识别GIF文件格式
  2. ​资源解码​GifDrawableGifFrameLoader负责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调用GifDrawableclear() - 停止动画 - 回收所有帧资源 - 释放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();     
} 
} 
相关推荐
安东尼肉店1 小时前
Android compose屏幕适配终极解决方案
android
2501_916007472 小时前
HTTPS 抓包乱码怎么办?原因剖析、排查步骤与实战工具对策(HTTPS 抓包乱码、gzipbrotli、TLS 解密、iOS 抓包)
android·ios·小程序·https·uni-app·iphone·webview
feiyangqingyun3 小时前
基于Qt和FFmpeg的安卓监控模拟器/手机摄像头模拟成onvif和28181设备
android·qt·ffmpeg
用户2018792831677 小时前
ANR之RenderThread不可中断睡眠state=D
android
煤球王子7 小时前
简单学:Android14中的Bluetooth—PBAP下载
android
小趴菜82277 小时前
安卓接入Max广告源
android
齊家治國平天下7 小时前
Android 14 系统 ANR (Application Not Responding) 深度分析与解决指南
android·anr
ZHANG13HAO7 小时前
Android 13.0 Framework 实现应用通知使用权默认开启的技术指南
android
【ql君】qlexcel7 小时前
Android 安卓RIL介绍
android·安卓·ril
写点啥呢7 小时前
android12解决非CarProperty接口深色模式设置后开机无法保持
android·车机·aosp·深色模式·座舱