一文看懂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...

相关推荐
Kapaseker1 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴1 小时前
Android17 为什么重写 MessageQueue
android
阿巴斯甜1 天前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android