GIF 播放是我司应用市场类应用的一个重要的功能,之前做图片加载框架技术选型时也是以加载 GIF 的性能为基准来进行选择的,GIF 播放功能在手机端比较常见,通常交给支持 GIF 的图片加载框架处理就行,但是对于开发板端而言,这类功能对内存和 CPU 有较大的挑战,稍微不注意就很容易造成 ANR 和应用崩溃。
背景
GIF播放主要是为了更加生动的展示广告,吸引用户查看与点击,增加推荐位的曝光率等,属于我司规划了很久的一个功能,但是为了应对杂乱无章的满屏的GIF播放以及其带来的高内存和CPU高负载的挑战,GIF播放需加以控制使其顺序播放,同时,之前的性能优化在Android 6.0及以下使用Fresco加载,其它的使用Glide加载,这一情况更加增加了该功能的复杂性,再者图片的父容器中包含RecyclerView和ViewPager,也要兼顾触屏滑动时的用户体验。
构思与实现
整个流程分为辨别GIF、加入队列、确定顺序,播放控制、特殊情况处理、兼容性处理和其他的细节处理等步骤。
基本情况
页面总体上的一个架构是ViewPager+Fragemnts的结构,每一个Fragment里是用RecyclerView展示的是类图片瀑布流,这是一种比较常见的平板或TV上的主页布局结构,其中部分位置根据后台数据的配置展示是GIF图片。当满足当前屏幕里的所有GIF都加载成功则开始按位置的顺序依次播放。
影响因素
在GIF播放整个流程中涉及到很多影响播放控制的点,这里我们把它们一一列举出来:
Fragment的onResume、onPause和onDestory方法GIF加载的的状态,像开始、成功和失败等状态ViewPager和RecyclerView的滑动状态- 影响图片控件状态的
attachedToWindow和detachedFromWindow方法
以上这些点或推进或影响整体的GIF播放控制
代码封装
根据以上列举的点,我们很轻易的得出播放控制的接口类
1 | public interface IGifPlayController { |
部分方法及参数说明:
resume和pause的两个方法分别对应可上下滑动的Fragment和整页显示的FragmentuniqueTag是在Adapter的onBindViewHolder方法里调用View.setTag()设置的,其中包含了pageId和position相对位置信息attachedToWindow和detachedFromWindow的两个方法对应的则是RecyclerView里不同的viewType布局类型notifyScrollStateChanged方法则只有newState等于SCROLL_STATE_IDLE时重新播放当前屏幕里展示的GIF
出于性能的考虑,目前加载GIF时先静态加载GIF的第一帧,等到真正的轮到当前GIF播放时再动态加载GIF并播放。
为此还定义了一个播放状态的实体类
1 | public class GifPlayDTO { |
对于兼容性的处理则是根据Fresco和Glide的特性,分别实现IGifPlayController接口,虽然加载图片的 API 不一样,但总体流程的处理上是一致的。
简单图示
当一个页面初次展示GIF时,Controller的大致处理流程和下图一致

sequenceDiagram
participant User as 用户
participant App as 应用程序
participant Glide as Glide库
User->>App: 加载GIF列表
App->>Glide: 使用asBitmap()显示Gif的首帧
Glide-->>App: 成功加载Gif的首帧
App->>App: copy首帧Bitmap并创建BitmapDrawable
loop 屏幕内GIF轮播
App->>App: 检查屏幕内的GIF
App->>App: 按顺序选取一个GIF
App->>Glide: 使用asGif()加载Gif并以
BitmapDrawable作为placeholder
Glide-->>App: 成功加载Gif后并做一些播放设置
App->>App: 播放GIF
App->>App: 播放完成
App->>Glide: 使用asBitmap()加载Gif首帧
并使用BitmapDrawable作为placeholder
Glide-->>App: 控件显示回首帧
end
App-->>User: 完成GIF列表加载与轮播
虽然一些细节不能体现出来,但大体上差不多。
总结
其实使用GIF来展示动图并不是第一优选,像Lottie、WebP以及SVGA等都可以展示动图,之所以没有考虑这些方案有以下原因:
- 没有相应的配套,需要设计和后端人员的支持,特别对设计的要求比较高
- 国内公司的技术路径依赖,
GIF播放是经过实践且成熟的方案,其它三个方案并没有在开发板上得到过验证,可能有兼容或其它的一些问题 - 其它三个方案一样要做顺序播放控制,且不一定与现有方案兼容