先把下面这两步的代码看了
GlideDrawableImageViewTarget
调用加载的
GifDrawable
来启动动画
GifDrawable
会在
draw()
中绘制当前帧, 并委托
GifFrameLoader
去加载下一帧
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
if (!resource.isAnimated()) {
float viewRatio = view.getWidth() / (float) view.getHeight();
float drawableRatio = resource.getIntrinsicWidth() / (float) resource.getIntrinsicHeight();
if (Math.abs(viewRatio - 1f) <= SQUARE_RATIO_MARGIN
&& Math.abs(drawableRatio - 1f) <= SQUARE_RATIO_MARGIN) {
resource = new SquaringDrawable(resource, view.getWidth());
super.onResourceReady(resource, animation);
this.resource = resource;
resource.setLoopCount(maxLoopCount);
resource.start();
public void start() {
isStarted = true;
resetLoopCount();
if (isVisible) {
startRunning();
private void startRunning() {
if (decoder.getFrameCount() == 1) {
invalidateSelf();
else if (!isRunning) {
isRunning = true;
frameLoader.start();
invalidateSelf();
至此, 我们触发了 frameLoader.start()
, 并且界面上目前也因为 invalidateSelf()
而绘制上了第一帧
再来看看第三步: 循环加载帧, 并渲染到界面上
GifFrameLoader
依赖 GifDecoder
加载完成下一帧通知 GifDrawable
刷新视图
public void start() {
if (isRunning) {
return;
isRunning = true;
isCleared = false;
loadNextFrame();
private void loadNextFrame() {
if (!isRunning || isLoadPending) {
return;
isLoadPending = true;
gifDecoder.advance();
long targetTime = SystemClock.uptimeMillis() + gifDecoder.getNextDelay();
DelayTarget next = new DelayTarget(handler, gifDecoder.getCurrentFrameIndex(), targetTime);
requestBuilder
.signature(new FrameSignature())
.into(next);
直接触发 loadNextFrame()
去加载下一帧, 真正的代码则是
gifDecoder.advance()
可以粗略理解成跳到下一帧头部位置gifDecoder.getNextDelay()
获得下一帧的间隔时间requestBuilder.into()
异步解析下一帧, 解析完成会回调 DelayTarget
下面看看 DelayTarget
里面的回调
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
this.resource = resource;
Message msg = handler.obtainMessage(FrameLoaderCallback.MSG_DELAY, this);
handler.sendMessageAtTime(msg, targetTime);
private class FrameLoaderCallback implements Handler.Callback {
public static final int MSG_DELAY = 1;
public static final int MSG_CLEAR = 2;
@Override
public boolean handleMessage(Message msg) {
if (msg.what == MSG_DELAY) {
GifFrameLoader.DelayTarget target = (DelayTarget) msg.obj;
onFrameReady(target);
return true;
} else if (msg.what == MSG_CLEAR) {
GifFrameLoader.DelayTarget target = (DelayTarget) msg.obj;
Glide.clear(target);
return false;
void onFrameReady(DelayTarget delayTarget) {
if (isCleared) {
handler.obtainMessage(FrameLoaderCallback.MSG_CLEAR, delayTarget).sendToTarget();
return;
DelayTarget previous = current;
current = delayTarget;
callback.onFrameReady(delayTarget.index);
if (previous != null) {
handler.obtainMessage(FrameLoaderCallback.MSG_CLEAR, previous).sendToTarget();
isLoadPending = false;
loadNextFrame();
DelayTarget.onResourceReady()
加载下一帧完成了;handler.sendMessageAtTime
切回主线程, 顺便还把 帧的时间间隔问题解决了callback.onFrameReady()
通知 callback 也就是 GlideDrawable 加载好了
下面来看看 GlideDrawable 在被通知加载好了之后做了些啥
public void onFrameReady(int frameIndex) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && getCallback() == null) {
stop();
reset();
return;
invalidateSelf();
if (frameIndex == decoder.getFrameCount() - 1) {
loopCount++;
if (maxLoopCount != LOOP_FOREVER && loopCount >= maxLoopCount) {
stop();
public void draw(Canvas canvas) {
if (isRecycled) {
return;
if (applyGravity) {
Gravity.apply(GifState.GRAVITY, getIntrinsicWidth(), getIntrinsicHeight(), getBounds(), destRect);
applyGravity = false;
Bitmap currentFrame = frameLoader.getCurrentFrame();
Bitmap toDraw = currentFrame != null ? currentFrame : state.firstFrame;
canvas.drawBitmap(toDraw, null, destRect, paint);
- 下一帧加载好了之后, GlideDrawable 触发自己的
draw()
方法开始绘制 frameLoader.getCurrentFrame()
取出下一帧canvas.drawBitmap()
直接往界面上绘制
至此, glide gif的加载实现已讲解完毕! 感谢观看
个人博客传送门一、涉及类目GlideDrawableImageViewTarget.javaGifDrawable.javaGifFrameLoader.javaGifDecoder.java二、原理概述老规矩先介绍原理的框架,免得看源代码迷路GlideDrawableImageViewTarget 会调用加载的 GifDrawable 来启动动画GifDrawable 会在 draw() 中绘制当前帧, 并委托 GifFrameLoader 去加载下一帧GifFrameLoader
Glide是一个面向Android的快速高效的开源媒体管理和图像加载框架,它将媒体解码、内存和磁盘缓存以及资源池封装到一个简单易用的接口中;
Glide支持获取、解码和显示视频静像、图像和动画GIF。Glide包括一个灵活的API,允许开发人员插入几乎任何网络堆栈。默认情况下,Glide使用自定义的基于HttpUrlConnection的堆栈,但也包括插件到Google的Volley项目或Square的OkHttp库中的实用程序库。
Glide的主要重点是尽可能平滑和快速地滚动
Android图片框架Glide-3.7.0(最新,很强大),超好用的图片框架(包含jar和源码)
Glide 是一个高效、开源、 Android设备上的媒体管理框架,它遵循BSD、MIT以及Apache 2.0协议发布。Glide具有获取、解码和展示视频剧照、图片、动画等功能,它还有灵活的API,这些API使开发者能够将Glide应用在几乎任何网络协议栈里。创建Glide的主要目的有两个,一个是实现平滑的图片列表滚动效果,另一个是支持远程图片的获取、大小调整和展示。近日,Glide 3.0发布,现已提供 jar包下载 ,同时还支持使用Gradle以及Maven进行构建。该版本包括很多值得关注的新功能,如支持Gif 动画和视频剧照解码、智能的暂停和重新开始请求、支持缩略图等,具体新增功能如下如下:
GIF 动画的解码 :通过调用Glide.with(context).load(“图片路径“)方法,GIF动画图片可以自动显示为动画效果。如果想有更多的控制,还可以使用Glide.with(context).load(“图片路径“).asBitmap()方法加载静态图片,使用Glide.with(context).load(“图片路径“).asGif()方法加载动画图片
本地视频剧照的解码: 通过调用Glide.with(context).load(“图片路径“)方法,Glide能够支持Android设备中的所有视频剧照的加载和展示
缩略图的支持: 为了减少在同一个view组件里同时加载多张图片的时间,可以调用Glide.with(context).load(“图片路径“).thumbnail(“缩略比例“).into(“view组件“)方法加载一个缩略图,还可以控制thumbnail()中的参数的大小,以控制显示不同比例大小的缩略图
Activity 生命周期的集成: 当Activity暂停和重启时,Glide能够做到智能的暂停和重新开始请求,并且当Android设备的连接状态变化时,所有失败的请求能够自动重新请求
转码的支持: Glide的toBytes() 和transcode() 两个方法可以用来获取、解码和变换背景图片,并且transcode() 方法还能够改变图片的样式
动画的支持: 新增支持图片的淡入淡出动画效果(调用crossFade()方法)和查看动画的属性的功能
OkHttp 和Volley 的支持: 默认选择HttpUrlConnection作为网络协议栈,还可以选择OkHttp和Volley作为网络协议栈
其他功能: 如在图片加载过程中,使用Drawables对象作为占位符、图片请求的优化、图片的宽度和高度可重新设定、缩略图和原图的缓存等功能
1. 首先,您需要下载Glide-3.7.0.jar文件并将其保存在您的项目文件夹中。
2. 在Android Studio中,右键单击您的项目文件夹,然后选择“Open Module Settings”。
3. 在“Modules”选项卡中,选择您的应用程序模块,然后单击“Dependencies”选项卡。
4. 单击“+”按钮,然后选择“File dependency”。
5. 在弹出的对话框中,选择您刚刚下载的Glide-3.7.0.jar文件。
6. 单击“OK”按钮,然后单击“Apply”按钮。
7. 现在,您已经成功添加了Glide-3.7.0.jar框架,可以在您的项目中使用它了。