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();     
} 
} 
相关推荐
帅得不敢出门6 小时前
Android Framework打电话禁止播放运营商视频彩铃
android·java·framework·音视频
非凡ghost6 小时前
Solid Explorer文件管理器:功能强大的安卓文件管理器及网盘文件管理器
android·学习·软件需求
独自破碎E6 小时前
得物25年春招-安卓部分编程题
android·java·开发语言
用户2018792831676 小时前
StickerHeaderRecyclerView的一种简易实现
android
张风捷特烈9 小时前
FlutterUnit 3.3.0 | 全组件、全属性、鸿蒙支持来袭
android·前端·flutter
咖啡の猫9 小时前
Android开发简介
android
咖啡の猫11 小时前
Android开发-工程结构
android
_祝你今天愉快17 小时前
Android Binder 驱动 - Media 服务启动流程
android