写点什么

Android Glide 3,flutter 小程序

用户头像
Android架构
关注
发布于: 1 小时前

二、原理概述

老规矩先介绍原理的框架,免得看源代码迷路



  • 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


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


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();

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android Glide 3,flutter小程序