从零开始撸一个Fresco之gif和Webp动画

简介: 上一篇文章的链接:从零开始撸一个Fresco之硬盘缓存转载请注明出处Fresco源代码文档翻译项目请看这里:Fresco源代码翻译项目 这个项目会不断更新想学习Fresco源代码的同学一定不要错过。

上一篇文章的链接:从零开始撸一个Fresco之硬盘缓存
转载请注明出处
Fresco源代码文档翻译项目请看这里:Fresco源代码翻译项目 这个项目会不断更新想学习Fresco源代码的同学一定不要错过。
Fresco中有个很重要的功能就是gif和Webp动画的实现,今天我就来讲解一下这个模块,顺便撸了个模块demo出来。这是项目的github地址Fresco动画模块,推荐看博客的时候结合项目一起看,项目中绝大部分类都有细致的注释,看起来还是很清晰的。

一、项目包结构

  • 1.animated:
    • 1.gif:这个包中的两个类都使用了jni代码,GifImage有两个功能:1.用于将Gif动画已解码数据储存在jni代码管理的本地内存中2.通过jni代码解析未解码的Gif数据。GifFrame则是储存Gif动画单个帧的数据也是通过jni代码管里的本地内存。
    • 2.webp:WebPImage类似前面的GifImage,只不过换成了Webp的数据。WebPFrame同理类似GifFrame
  • 2.bitmapFactory:无论是动画的帧还是静态的图片,最后都需要创建为Bitmap显示在View上。而不同Android版本创建Bitmap的方式是不同的,这里的工厂不同的工厂就是用于不同Android版本创建Bitmap这里的工厂用到了后面的platformDecoder包中的解码器,platformDecoder包中的类才是在不同Android版本下创建Bitmap的具体代码逻辑。
  • 3.cacheKey:每一个Key对应一个图片或动画。
  • 4.common:
    • 1.nativeLoader:每一个Key对应一个图片或动画。
    • 2.s:不同类的工具方法,如Ints中有int的工具方法。
    • 3.stream:简单的基于Java流的包装类
    • 4.util:工具类
  • 5.excutor:
    • 1.executorSupplier:包的实现在DefaultExecutorSupplier中,用于提供不同的Executor
    • 2.handlerExcutor:用于提供转移到Handler线程的Excutor,唯一实现是UiThreadImmediateExecutorService,转到主线程。
    • 3.serailExcutor:用于提供串行执行任务的Excutor,DefaultSerialExecutorService是具体实现。
  • 6.image:包对外的实现是CloseableAnimatedImage和CloseableStaticBitmap,一个用于封装动画数据,一个用于封装静态图片信息。
    • 1.imageDecode:AnimatedImageDecoder的实现为animated包下的GifImage和WebPImage,用于解码未解码的动画数据。ImageDecoder的实现是DefaultImageDecoder用于解码所有图像数据,其用到了AnimatedImageFactoryImpl以提供CloseableAnimatedImage。
    • 2.imageFormat:这个包用于检测EcodingImage类中的数据是什么类型的图像数据。
    • 3.imageInfo:用于储存图片的简单信息和图片目前的质量。
  • 7.imagepipeline:
    • 1.memory:NativeMemoryChunk是本地内存块的java封装,用于提供一块本地内存,这里的本地内存使用jni代码管理。NativePooledByteBuffer则是基于NativeMemoryChunk提供了一个字节池,用来提供可回收使用的字节数组。
    • 2.nativecode:各种使用到了jni代码的java封装类
  • 8.platformDecoder:bitmapFactory包中各个工厂生成Bitmap的具体实现包。
  • 9.pool:这个包里是各种资源可回收使用的对象池子,如Bitmap和Byte数组等等。这样的好处是减少内存频繁GC带来的卡顿。
    • 1.poolFactory:这个包的对外实现是PoolFactory,用于提供各种Pool。
    • 2.poolParams:这个包中提供各种Pool的参数。
    • 3.poolUtilpool的工具包,BitmapCounter用于计数Bitmap,保持跟踪总的byte的数量,和Bitmap数量。PoolStatsTracker用来跟踪各种Pool的操作。
  • 10.refrence:包对外提供的实现是CloseableReference,以引用计数的方式将一些可关闭的大数据块关闭。类似JVM的内存回收,当引用计数为0时,内存会自动释放。
  • 11.webpsupport:在Android2.3以下是不支持Webp的,这个包中的类就是用来让2.3一下的Android机可以使用Webp。
  • 12:factoryAndProvider:这个包就是动画的主要实现逻辑, 最终提供的是AnimatedDrawable类,这个类只要直接设置在Veiw上就能使View显示Gif或者Webp的动画。这个包我使用树状层次来描述各个类之间的使用关系,所以比较复杂,大家可以结合后面的图片一起观看。AnimatedFactoryProvider用于提供一个AnimatedFactory
    • 1.animatedFactory:AnimatedFactory用于返回创建一个Gif和Webp动画的两个重要工厂:AnimatedDrawableFactory和AnimatedImageFactory。AnimatedFactoryImpl是具体实现。
      • 1.animatedImageFactory:AnimatedImageFactory用于将EncodedImage这个未解码的Gif或者Webp的数据类,解码成CloseableImage,如果解码成功这里的CloseableImage的实现是CloseableAnimatedImage。AnimatedImageFactoryImpl是AnimatedImageFactory的具体实现。
        • 1.animatedImage:这里个包的主要实现是AnimatedImageResult,上一级目录中说的CloseableAnimatedImage中的Gif和Webp解码后的数据就是用这个AnimatedImageResult包装。其内部储存了animated包中的类:整个动画、每帧画面和预览帧。
      • 2.animatedDrawableFactory:这里的AnimatedDrawableFactory用于将AnimatedImageFactory提供的CloseableImage解析成AnimatedDrawable。具体实现是AnimatedDrawableFactoryImpl。
        • 1.animatedDrawable:包对外提供的实现是AnimatedDrawable,该类实现了Drawable,用于将AnimatedDrawableBackend提供的动画数据绘制在该Drawable设置测View上。
        • 2.animatedBackend用于封装CloseableAnimatedImage提供的AnimatedImageResult。包对外提供的实现是AnimatedDrawableBackendImpl和AnimatedDrawableCachingBackendImpl,AnimatedDrawableCachingBackendImpl用于在AnimatedDrawableCachingBackend之上封装一个缓存功能。
        • 3.other:一些辅助AnimatedDrawable的类

二、对象池Pool

先来讲讲pool包中的对象池,对象池有什么用?当我们使用一个频繁创建和销毁的对象的时候,为了减少创建和销毁对象所带来的消耗,我们可以维持一个该对象的集合,当不使用的时候将对象放回集合中,使用的时候直接获取引用赋予值。一个典型的对象池就是线程池。在Fresco中由于要频繁地对Bitmap进行操作,所以对Bitmap我们可以使用对象池,此外还有byte数组等。

  • 1.先来介绍Pool所用到的数据结构:
    • 1.以Bitmap为例要重新使用一个Bitmap,就需要预期的Bitmap与重用的Bitmap使用的内存字节数相同或者重用的大于预期的,只有这样预期的Bitmap才能完整地放入被重用的Bitmap中。所以这里我们用到了SparseArray(稀疏数组)和Bucket(桶,自定义的类,内部使用了LinkedList)。首先SparseArray的下标表示内存的字节数,由于字节数一般跨度比较大所以使用了SparseArray。SparseArray中储存着Bucket,Bucket表示当两个可以被重用的Bitmap字节数相同时,使用LinkedList进行排列储存。下面的图简单的描述了一下这个数据结构。
    • 2.上面的这一个Pool是基于Java内存分配,但是我们都知道一个app能使用的内存是有限制的,因为使用new和创建Bitmap的时候使用的内存都是通过dalvik虚拟机在java堆上分配内存的。Android系统设置了一个Java堆的阈值(48M、24M、16M等)当超出之后就会报OOM。而使用jni代码在native heap上面可以申请的内存却是不受限制的(只受整个手机的内存限制)。所以Fresco当然使用了这个方式以提供Byte数组池。具体封装了jni管理的本地内存的类是imagePipline.memory包下的NativeMemoryChunk类。这里的NativeMemoryChunk只替代了1中申请内存的方式,其他方面不变。
  • 2.总结:在Fresco中一般的静态图片的数据使用的是BitmapPool,这里使用的是java堆上的内存。而动态图片类似Gif和Webp,则是使用Native内存

三、AnimatedDrawable

上面的图是factoryAndProvider包中类的结构示意图,一定要结合项目一起观看。AnimatedDrawable顾名思义就是一个可以显示动画的Drawable。Android的View在设计的时候为了让Drawable能够实现动画,特意实现了Drawable.CallBack接口。这个接口可以让Drwable对View显示的图像进行调度。AnimatedDrawable就是通过这个机制实现动画的。

  • 1.如何创建一个AnimatedDarwable,由上面的图可以看出有以下几个步骤:
    • 1.AnimatedFactoryProvider提供一个AnimatedFactoryImpl。
    • 2.AnimatedFactoryImpl提供一个AnimatedDrawableFactoryImpl和AnimatedImageFactoryImpl。
    • 3.AnimatedImageFactoryImpl将EncodedImage通过GifImage和WebpImage转换成一个AnimatedImageResult并用CloseableAnimatedImage包装。
    • 4.AnimatedDrawableFactoryImpl通过传入的CloseableAnimatedImage获取AnimatedImageResult,然后包装成一个AnimatedDrawableCachingBackendImpl。
    • 5.AbstractAnimatedDrawable继承了Drawable然后内部集成了AnimatedDrawableCachingBackendImpl。最后通过Drawable.CallBack接口在View上绘制AnimatedDrawableCachingBackendImpl中提供的每一帧的数据。
    • 6.以上就是简述AnimatedDrawable创建的全过程,项目中有详细的注释,大家可以跟着上面这几个步骤观看项目源码
  • 2.AnimatedDrawable显示动画的流程:我在项目中的AbstractAnimatedDrawable类的开头,详细地描述了AnimatedDrawable的两种启动动画的方式,大家可以顺着项目中描述的路线观看

四、项目使用

在administrator.myanimated包下有个MainActivity,用来演示png、jpg、静态webp、动态webp、gif这五种图像的展示。大家在使用的时候记得将自己准备的这个几种文件按命名,放入app的缓存文件夹里。

相关文章
|
存储 人工智能 运维
重磅发布!飞天智算平台+全球最大智算中心
阿里云宣布正式推出全栈智能计算解决方案“飞天智算平台”,并启动两座超大规模智算中心,为科研、公共服务和企业机构,提供强大的智能计算服务,可将计算资源利用率提高3倍以上,AI训练效率提升11倍,推理效率提升6倍。
8305 1
重磅发布!飞天智算平台+全球最大智算中心
|
Android开发
android Compose中沉浸式设计、导航栏、状态栏的处理
android Compose中沉浸式设计、导航栏、状态栏的处理
2662 0
android Compose中沉浸式设计、导航栏、状态栏的处理
|
4月前
|
安全 网络安全 数据安全/隐私保护
DirectX修复工具增强版,免费的dll修复工具,dll下载,DirectX修复工具下载
DirectX修复工具是一款系统级工具软件,支持Windows XP至Windows 11多个操作系统版本,兼容32位和64位系统。程序可自动调整任务模式,无需用户设置,操作简便,点击即可修复DirectX问题。增强版还支持修复C++运行库问题,提供在线修复版和标准版多种选择。遇到如英雄联盟game_error_directx错误或0xc000007b问题时,使用该工具可有效解决。程序具备扩展功能,可通过下载数据包升级为增强版,并提供详细错误提示与修复方案,适用于多种DirectX及C++异常情况。
371 4
|
8月前
|
Android开发 开发者 Kotlin
Android实战经验之Kotlin中快速实现MVI架构
MVI架构通过单向数据流和不可变状态,提供了一种清晰、可预测的状态管理方式。在Kotlin中实现MVI架构,不仅提高了代码的可维护性和可测试性,还能更好地应对复杂的UI交互和状态管理。通过本文的介绍,希望开发者能够掌握MVI架构的核心思想,并在实际项目中灵活应用。
343 8
|
JSON 数据处理 数据安全/隐私保护
Ktor库的高级用法:代理服务器与JSON处理
Ktor库的高级用法:代理服务器与JSON处理
|
NoSQL Java MongoDB
【MongoDB 专栏】MongoDB 与 Spring Boot 的集成实践
【5月更文挑战第11天】本文介绍了如何将非关系型数据库MongoDB与Spring Boot框架集成,以实现高效灵活的数据管理。Spring Boot简化了Spring应用的构建和部署,MongoDB则以其对灵活数据结构的处理能力受到青睐。集成步骤包括:添加MongoDB依赖、配置连接信息、创建数据访问对象(DAO)以及进行数据操作。通过这种方式,开发者可以充分利用两者优势,应对各种数据需求。在实际应用中,结合微服务架构等技术,可以构建高性能、可扩展的系统。掌握MongoDB与Spring Boot集成对于提升开发效率和项目质量至关重要,未来有望在更多领域得到广泛应用。
367 3
【MongoDB 专栏】MongoDB 与 Spring Boot 的集成实践
|
编解码 Linux 数据安全/隐私保护
深度探索:使用FFmpeg实现视频Logo的添加与移除(一)
深度探索:使用FFmpeg实现视频Logo的添加与移除
471 0
|
消息中间件 JavaScript Java
消息队列 MQ产品使用合集之如何嵌入到Spring Boot中运行
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
Kubernetes Shell Docker
K8S核心插件-Flannel网络插件
K8S核心插件-Flannel网络插件
500 0
|
SQL 关系型数据库 MySQL
事务隔离大揭秘:MySQL中的四种隔离级别解析
事务隔离大揭秘:MySQL中的四种隔离级别解析
3221 0