Android Glide GifDrawable 模块源码解析
来源:互联网 发布:adblock plus for mac 编辑:程序博客网 时间:2024/05/22 11:36
背景
项目有个需求:对GIF动图实时添加文字。
结合之前FollowMe景区导游助手项目中做过图片添加水印,MovieMaker项目中多张图片合成一段具有多种切换效果视频的经验,预测需要对GIF文件的数据进行解码,合成处理,编码输出合成文件。
项目基于Glide库实现GIF图片的显示,因此需要搞清楚GIF文件的数据是怎么显示到ImageView模块。本文记录分析GifDrawable模块的原理的过程。
问题:
- ImageView中的图片是怎么动起来的?
- 如果不使用第三方库,自己实现GIF显示,应该怎么设计及实现?其中有哪些坑?
- GifDrawable模块是什么?为什么需要有这个模块?该模块的输入、输出数据是什么?该模块在系统中处于什么样的层次?模块内部的数据结构和算法是什么?
猜测:
如果是自定义View中的图片需要动起来,则需要主线程每隔一定时间在Canvas上画不同的Bitmap。
GifDrawable 简介
以下基于2016/11/01 master版本代码。
数据结构
GifDrawable GifState BitmapPool GifFrameLoader
类层次结构
Object Drawable GifDrawable
GifDrawable本质是个Drawable,属于View中的子模块/底层模块。其中View和Drawable的层次关系:
View--------------------控件树中一个节点,类似一个框Drawable----------------可以画内容,类似框中的内容ConstantState-----------多个Drawable可能共享的一段数据
另外GifDrawable还实现了两个接口:
public class GifDrawable extends Drawable implements GifFrameLoader.FrameCallback, Animatable
其中FrameCallback接口是底层的回调,也就是底层每解码出来一帧就会给上层一个消息,采用回调来解耦:
public interface FrameCallback { void onFrameReady(); }
再开GifDrawable在接收到底层回调的时候运行什么代码:
@TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override public void onFrameReady() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && getCallback() == null) { stop(); invalidateSelf(); return; } //把自己的矩形区域置为失效,触发下一次View的draw invalidateSelf(); if (getFrameIndex() == getFrameCount() - 1) { loopCount++; } if (maxLoopCount != LOOP_FOREVER && loopCount >= maxLoopCount) { stop(); } }
既然GifDrawable是个可以画的模块,那它到底画什么?
@Override public void draw(Canvas canvas) { if (isRecycled) { return; } if (applyGravity) { Gravity.apply(GifState.GRAVITY, getIntrinsicWidth(), getIntrinsicHeight(), getBounds(), getDestRect()); applyGravity = false; } Bitmap currentFrame = state.frameLoader.getCurrentFrame(); //每次在canvas上画当前帧的Bitmap canvas.drawBitmap(currentFrame, null, getDestRect(), getPaint()); }
对于Animate接口的实现:
private void startRunning() { Preconditions.checkArgument(!isRecycled, "You cannot start a recycled Drawable. Ensure that" + "you clear any references to the Drawable when clearing the corresponding request."); // If we have only a single frame, we don't want to decode it endlessly. if (state.frameLoader.getFrameCount() == 1) { invalidateSelf(); } else if (!isRunning) { isRunning = true; //底层FrameLoader开始工作,并且持有该GifDrawable的引用。用于给onFrameReady的回调。 state.frameLoader.subscribe(this); invalidateSelf(); } }
下层subscribe接口的实现:
//上层向下层注册/订阅回调 void subscribe(FrameCallback frameCallback) { if (isCleared) { throw new IllegalStateException("Cannot subscribe to a cleared frame loader"); } boolean start = callbacks.isEmpty(); if (callbacks.contains(frameCallback)) { throw new IllegalStateException("Cannot subscribe twice in a row"); } //把上层的多个回调放到一个容器中装起来,以便之后一个个告诉上层 callbacks.add(frameCallback); if (start) { //启动该模块 start(); } }
GifDrawable模块总结
- Client调用start后,启动下层的FrameLoader
- FrameLoader按照Gif数据流中的帧间隔往主线程中放消息,回调onFrameReady
- GifDrawable每次收到下层的消息时把自己的矩形区域置为dirty状态,触发draw回调
- 每次draw的时候,去当前帧的Bitmap,画到Canvas画布上,这样实现动画
需要注意的点:
- 该模块自身内部数据结构包含哪些数据,也就是一块内存,可以通过debug模式看动态内存
- 该模块上层调用者是View,依赖的下层是FrameLoader
- 模块的启动、停止过程干了什么事情
- 内部消息/数据流向是怎么样的
- 涉及到哪些线程,每个API/每行代码是运行在哪个线程中,是否需要加锁
0 0
- Android Glide GifDrawable 模块源码解析
- Android Glide源码解析
- Android Glide源码解析
- Android Glide源码解析
- Android Glide源码解析
- Android第三方库:Glide源码解析
- Glide 源码解析
- Glide 源码解析
- Glide 源码解析
- Glide源码解析
- Glide源码解析
- Glide源码解析
- glide:源码解析
- Glide源码解析
- 深入解析Glide源码
- Glide缓存源码解析
- Glide源码解析
- Glide V4源码解析
- iOS App 唤醒另一个App
- iOS开发中WiFi相关功能总结
- CAS单点登录实战
- 和各种诡异 Bug 打交道 13 年,我总结了 18 个经验
- Spring定时任务的几种实现
- Android Glide GifDrawable 模块源码解析
- 两个字符串连接
- 测试经验之谈---001
- 交互须知的HTTP协议
- jstack(查看线程)、jmap(查看内存)和jstat(性能分析)命令
- 你的名字。
- 实现外卖选餐时两级tableView联动效果
- Android 相机拍照获取图片并保存到指定位置。
- java 不循环输出数组,逗号隔开