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

相关推荐
pengyu10 小时前
【Kotlin 协程修仙录 · 大乘境 · 初阶】 | 跳出三界:协程在 KMP 与后端开发中的跨平台之道
android·kotlin
XiaoLeisj10 小时前
Android Memory Profiler:堆内存指标、内存抖动与泄漏定位
android·性能优化·内存抖动·内存泄露·memory profiler
未来猫咪花10 小时前
Everything is ViewModel:让状态管理回到对象世界
android·flutter·ios
古法安卓10 小时前
Android-重启流程源码解析
android·java·android studio
协议的旁观者11 小时前
Android 高级逆向实战(一):对抗 360 企业加固,Native 抽取还原与 Activity 生命周期重建
android
mmsx13 小时前
osmdroid 屏幕坐标与测量坐标互转:中心点+比例尺仿射换算
android·源码·地图·osmdroid
00后程序员张14 小时前
Android证书绑定抓包失败?Android SSL Pinning绕过实战指南
android·网络协议·计算机网络·网络安全·adb·https·ssl
杉氧14 小时前
页面栈与路由:React Navigation 与 Expo Router 深度实践
android·前端·react native
XiaoLeisj14 小时前
Jetpack Compose 知识点
android·kotlin·android jetpack·compose
又见情义15 小时前
RK3568 Android 13开机时间优化-系统应用裁剪实践
android