一文看懂Fresco架构

梳理了Fresco的整体架构、网络请求流程、三级缓存策略。

第一节主要是源码追踪,可以直接跳过从第二节开始阅读。

加载框架代码

  • 官网示例代码

官方的示例代码,创建一个DraweeView,调用setImageUri就可以触发加载。

  • setImageURI

里面代码就是创建DraweeController。每次图片加载都会对应到一个DraweeController,它负责加载图片,以及将图片提供给DraweeView进行显示。

  • setController

DraweeView中和DraweeHolder中setController代码分别如下,这里也出现了一个新的对象DraweeHierarchey,暂时忽略它。

将DraweeView和DraweeHierarchey看成同一个东西,他们负责将Controller加载成功后或者其他默认图显示出来也就是view。

DraweeHolder相当于两者之间的桥梁,将View和Controller结合起来。

  • attachController

attachController()方法需要同时满足几个条件,设置了controller、控件可见、view.onAttach。

Controller的attach()代码在AbstractDraweeController中。调用了submitRequest()。在其中又调用DataSource开始加载。

scss 复制代码
// AbstractDraweeController.java

public void onAttach() {
    ......
    if (!mIsRequestSubmitted) {
        submitRequest();
    }
}

protected void submitRequest() {
    ......
    mDataSource = getDataSource();
    
    final DataSubscriber<T> dataSubscriber = ...
           
    mDataSource.subscribe(dataSubscriber, mUiThreadImmediateExecutor);

}
  • DataSource

追踪dataSource的来源,又找到了新东西ImagePipeline,先直接点进去看

到ImagePipeline里面,这个方法做了两件事,创建ProducerSequence,然后是submitFetchRequest()

ProducerSequence控制加载,Fresco的加载流程都是通过它完成

submitFetchRequest()方法一路看下去,会走AbstractProducerToDataSourceAdapter的构造方法,其中调用producer.produceResults()启动加载

加载框架流程

通过上一节代码的追踪,加载从DraweeView.setUri()开始,层层调用,经过controller层最终到了最底部的ProducerSequence开始了加载,加载成功后再层层回调到view层,具体流程:

这里要注意几个关键的部分:

  • DraweeView和DraweeHierarchey,他们负责将图显示出来,DraweeHolder将它们和DraweeController链接起来,是两者之间的桥梁,将两者结合起来以及两者之间做部分解耦
  • DataSource负责图片加载,直接由DraweeController控制,加载流程最终是ProducerSequence
  • DraweeController参与图片加载所有的流程

这样已经是很熟悉的MVC结构了,如图:

暂时无法在飞书文档外展示此内容

网络请求流程

知道了整体的结构,再来梳理数据加载流程,也就是ProducerSequence。

  • producer

先看producer的定义,一个只有一个方法的接口,context携带上下文信息,存储producer应该加载的图像的属性信息;consumer是消费者,通过它将加载状态以及加载结果回调出去。

再来看一个consumer的代理实现,本身作为Consumer,再通过构造方法持有另一个Consumer-A,通过对A的代理和增强,实现额外的功能和逻辑,属于装饰器的设计模式。

接着要来看一个Producer的实现,这里的代码来自ThrottlingProducer,去除了所有的处理逻辑,只留下了架构方面的代码。

跟上面类似,本身作为Producer-- A,再通过构造方法持有另一个Producer--B,在produceResults中作了一些逻辑处理之后,调用Producer--B的produceResults方法,传入的消费者consumer是代理了外部来源的consumer--c的xxxxConsumer--A。在xxxxConsumer--A的回调中,调用了consumer--c的回调之后再额外处理了一部分自身的业务逻辑。

设计上来说就是通过对Producer--B的持有,再回调consumer--c的基础上扩展了Producer-- A和Consumer--A的功能,属于代理模式。

java 复制代码
public class XXXXProducer<T> implements Producer<T> {

    private final Producer<T> mInputProducer;

    public XXXXProducer( final Producer<T> inputProducer) {
        ...
        mInputProducer = inputProducer;
    }

    @Override
    public void produceResults(final Consumer<T> consumer, final ProducerContext producerContext) {
        ...
        mInputProducer.produceResults(new XXXXConsumer(consumer), producerContext);
    }

    private class XXXXConsumer extends DelegatingConsumer<T, T> {

        private XXXXConsumer(Consumer<T> consumer) {
            super(consumer);
        }

        @Override
        protected void onNewResultImpl(T newResult, @Status int status) {
            getConsumer().onNewResult(newResult, status);
            ...
        }
    }
}

暂时无法在飞书文档外展示此内容

  • producerSequence

在第一节结尾的时候,停在了producerSequence的创建方法,调用ProducerSequenceFactoryd的getDecodedImageProducerSequence(),然后调用了getBasicDecodedImageSequence(),这里根据Uri的不同类型创建不同的producerSequence。

以network为例,getNetworkFetchSequence()中,newBitmapCacheGetToDecodeSequence(getCommonNetworkFetchToEncodedMemorySequence())方法看起来,就是外层包装BitmapCash的一个producer,里面继续创建Seqence,这样一层一层包起来一个链式的producer。

源码流程很多,这里不再贴出,整理了一下最终的ProducerSequence,getNetworkFetchSequence()最终的层级结构:

  1. BitmapMemoryCacheGetProducer,图片缓存读取

  2. ThreadHandoffProducer,线程切换

  3. BitmapMemoryCacheKeyMultiplexProducer,多路复用,相同请求进行合并

  4. BitmapMemoryCacheProducer,图片缓存

  5. ResizeAndRotateProducer,图片调整

  6. AddImageTransformMetaDataProducer,元数据解码

  7. EncodedCacheKeyMultiplexProducer,元数据多路复用

  8. EncodedMemoryCacheProducer,元数据缓存

  9. DiskCacheReadProducer,磁盘缓存读取

  10. DiskCacheWriteProducer,磁盘缓存写入

  11. WebpTranscodeProducer,webp转码

  12. NetworkFetchProducer,网络请求

这里不得不感叹Fresco本身的强大,一次网络请求竟然需要这么多的流程,通过这里Prodcer的排列,可以看出Fresco的缓存框架:

  1. 总共具有三层缓存

    1. 第一层Bitmap缓存,Bitmap缓存存储Bitmap对象,这些Bitmap对象可以立即用来显示,在线程切换之前就读缓存,缓存在内存当中,在后台会被清掉,Bitmap相对于元数据会大很多,参考之前的Bitmap相关知识

    2. 第二层元数据缓存,元数据缓存存储原始压缩格式图片如png、jpg,这些缓存在使用时需要先解码成bitmap,使用会再次缓存到第一层缓存,缓存在内存中,在后台会被清掉

    3. 第三层元数据缓存,与第二层的缓存数据完全一致,使用时需要解码,使用会再次缓存到第一层和第二层缓存中,缓存在磁盘中,在后台不回被清除

总结

学习和梳理了Fresco的整体架构和加载流程整理完成,但明显可以看出,这在它整个的设计中知识很小的一部分,这里还可以挖掘的点特别多,比如图片解码、缓存管理、磁盘管理。但有了这些框架的知识,后面继续学习这些更细节的东西也方便了很多。

参考文章

juejin.cn/post/684490...

相关推荐
AnalogElectronic2 小时前
问题记录,在使用android studio 构建项目时遇到的问题
android·ide·android studio
我爱松子鱼3 小时前
mysql之InnoDB Buffer Pool 深度解析与性能优化
android·mysql·性能优化
江上清风山间明月6 小时前
Flutter开发的应用页面非常多时如何高效管理路由
android·flutter·路由·页面管理·routes·ongenerateroute
子非衣9 小时前
MySQL修改JSON格式数据示例
android·mysql·json
openinstall全渠道统计12 小时前
免填邀请码工具:赋能六大核心场景,重构App增长新模型
android·ios·harmonyos
双鱼大猫13 小时前
一句话说透Android里面的ServiceManager的注册服务
android
双鱼大猫13 小时前
一句话说透Android里面的查找服务
android
双鱼大猫13 小时前
一句话说透Android里面的SystemServer进程的作用
android
双鱼大猫13 小时前
一句话说透Android里面的View的绘制流程和实现原理
android
双鱼大猫14 小时前
一句话说透Android里面的Window的内部机制
android