Android Glide 3,flutter 小程序
二、原理概述
老规矩先介绍原理的框架,免得看源代码迷路
GlideDrawableImageViewTarget
会调用加载的GifDrawable
来启动动画GifDrawable
会在draw()
中绘制当前帧, 并委托GifFrameLoader
去加载下一帧GifFrameLoader
依赖GifDecoder
加载完成下一帧通知GifDrawable
刷新视图
GifDrawable 其实是重写的 Drawable, 通过其 invalidateSelf() 通知界面重绘自己, 且在 draw() 方法中完成重绘, 还需要管理 loop, 用以控制结束循环 GifFrameLoader 负责控制加载每一帧的时间间隔, 还负责管理加载位置 GifDecoder 负责加载 gif 的帧
三、源码细节
先把下面这两步的代码看了
GlideDrawableImageViewTarget
调用加载的GifDrawable
来启动动画GifDrawable
会在draw()
中绘制当前帧, 并委托GifFrameLoader
去加载下一帧
// GlideDrawableImageViewTarget
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;// 这里调用了 GifDrawable 的 start 方法 resource.setLoopCount(maxLoopCount);resource.start();}
// GlideDrawable
public void start() {// 状态置换跳过不看 isStarted = true;resetLoopCount();if (isVisible) {// 真正的使能代码 startRunning();}}
private void startRunning() {// gif 只有 1 帧, 开始即结束 if (decoder.getFrameCount() == 1) {// 通知界面重绘自己, 结束了 invalidateSelf();}
// 不只 1 帧 else if (!isRunning) {isRunning = true;// frameLoader 开始工作啦 frameLoader.start();// 通知界面重绘自己, 就是把当前帧给先画出来 invalidateSelf();}}
至此, 我们触发了 frameLoader.start()
, 并且界面上目前也因为 invalidateSelf()
而绘制上了第一帧 再来看看第三步: 循环加载帧, 并渲染到界面上
GifFrameLoader
依赖GifDecoder
加载完成下一帧通知GifDrawable
刷新视图
// GifFrameLoader
public void start() {if (isRunning) {return;}isRunning = true;isCleared = false;
// 上面都是些状态信息, 跳过不看, 这个函数看名字就知道跑去加载下一帧了 loadNextFrame();}
private void loadNextFrame() {if (!isRunning || isLoadPending) {return;}isLoadPending = true;
// 这行是移动 gifDecoder 的解析位置, 跳到下一帧的位置 gifDecoder.advance();// 获取下一帧的延时时间(gif 每帧之间都有个时间间隔)long targetTime = SystemClock.uptimeMillis() + gifDecoder.getNextDelay();DelayTarget next = new DelayTarget(handler, gifDecoder.getCurrentFrameIndex(), targetTime);// 开始异步加载, 即不在主线程执行加载程序 requestBuilder.signature(new FrameSignature()).into(next);}
直接触发 `loadNextF
rame()` 去加载下一帧, 真正的代码则是
gifDecoder.advance()
可以粗略理解成跳到下一帧头部位置gifDecoder.getNextDelay()
获得下一帧的间隔时间requestBuilder.into()
异步解析下一帧, 解析完成会回调DelayTarget
下面看看 DelayTarget
里面的回调
// DelayTarget
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {this.resource = resource;// 解析完成了, resource 就存储着下一帧的图 Message msg = handler.obtainMessage(FrameLoaderCallback.MSG_DELAY, this);// 这里 MSG_DELAY 明显是处理帧的时间间隔, 现在要异步切回主线程处理刷新问题了 handler.sendMessageAtTime(msg, targetTime);}
private class FrameLoaderCallback implements Handler.Callback {public static final int MSG_DELAY = 1;public static final int MSG_CLEAR = 2;
@Overridepublic 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;}}
// GifFrameLoader
void onFrameReady(DelayTarget delayTarget) {if (isCleared) {handler.obtainMessage(FrameLoaderCallback.MSG_CLEAR, delayTarget).sendToTarget();return;}
DelayTarget previous = current;current = delayTarget;// callback 是一个 GlideDrawable, 告诉它我帮你把下一帧加载出来了, 下面来看看 GlideDrawable 是如何做的 callback.onFrameReady(delayTarget.index);
if (previous != null) {handler.obtainMessage(FrameLoaderCallback.MSG_CLEAR, previous).sendToTarget();
评论