fresco介绍
说到android图片加载框架,我们都熟悉glide
,Picasso
.fresco
等,由于我们公司使用了react-native
作为了主要的跨平台开发框架,其中自然而然的集成了facebook
的fresco
。所以,在这里主要分析一下fresco
是怎么加载图片的.
在使用方面fresco
是其中最复杂的,但是它的优势也是比较明显的,主要是在以下这几个方面:
图片的渐进式呈现。
支持动图加载:加载Gif、WebP动图,每一帧都是一张很大的Bitmap,每个动画都有很多帧。Fresco能管理好每一帧并管理好你的内存。
丰富的图片处理:缩放、圆角、透明、高斯模糊等处理。
在5.0以下系统,Bitmap缓存位于`ashmem,这样Bitmap对象的创建和释放将不会引发GC,更少的GC会使你的App运行得更加流畅。
良好的代码设计,代码可扩展性非常好。
fresco加载图片流程
我们知道图片加载框架,为了加快下次图片的加载速度,一般是有其缓存机制的,而fresco的缓存机制也和大多数图片加载库一样,有着memory cahce
&disk cache
,但是fresco
缓存策略比较复杂,分为:bitmap缓存,未解码图片的内存缓存,磁盘缓存。fresco
有着三级缓存。
注解:所谓图片是否解码,指的是图片是以bitmap的形式存在,还是二进制流的形式存在。未解码表示是二进制流的形式,解码表示将二进制流转为bitmap对象。
简易的加载流程如下,
因为在具体的图片加载逻辑中,设计到各种缓存,decode,裁剪图片等操作,流程是非常复杂的,下面是正常图片加载Producer和Consumer的流程。
关于这块的逻辑,fresco设计的非常巧妙,使用装饰器和责任链模式混用的方式,使得代码结构变得非常容易扩展,而且想要自己定义图片的加载顺序,只需要按照相应的顺序,嵌套new一个producer sequence就可以了。我们目前请求图片就是用的getDecodedImageProducerSequence
这个sequence.
1 | public DataSource<CloseableReference<CloseableImage>> fetchDecodedImage( |
fresco
有个producerSequenceFactory
有许多不同的producer sequence
,方便我们根据不同的场景选择不同的sequence。
关于producer和consumer的设计,我使用ts写了一个简单的实现,可以一块预览一下。
bitmapMemoryCache && encode Image Cache
bitmapMemoryCache
和EncodedMemoryCache
是使用的相同的数据结构,MemoryCache
来存储的。主要区别是bitmapMemoryCache
缓存的是CloseableImage
,而EncodedMemoryCache
缓存的是PooledByteBuffer
字节流。
LRUMap: Least Recently Used Map,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。 也常见于软件开发中,用于处理一些缓存策略。
fresco
中使用LinkedHashMap
实现LRU
算法,LinkedHashMap
是jdk
中,一个具有双链表的Map
,是一个有序的HashMap
,这里的有序指的是元素的插入顺序。
disk cache
fresco
磁盘缓存是分为SMALL
,DEFAULT
两种,这两种策略的都是用BufferedDiskCache
做的缓存,因此,直接分析这个类就可以了。
1 | public void put(final CacheKey key, EncodedImage encodedImage) { |
首先是按照[cacheKey,EncodeImage]的方式,将其缓存在Map中,接下来会开启一个线程,用来异步存储encodeImage到disk中。在这里通过图片的url,经过SHA1加密和base64转码,之后得到一个resourceId,主要用来生成存储文件的文件名,文件名格式为:/data/user/0/package/cache/image_cache/v2.ols100.1/95/mtQL41H7RDPU2uZo1zMCo5fto-Q.4178151137210219191.tmp
图片加载网络请求
fresco
关于图片的网络请求,抽象出来一个NetworkFetcher
接口来处理,因此,开发者可以通过继承NetworkFetcher
,使用自己熟悉的网络请求库来封装。fresco
内部已经有了volley
,okhttp
,httpUrlConnection
三种网络库的实现。
ReactImageView是如何加载的?
1.1qrn
是如何加载的?
首先我们先分析一下qrn
是如何加载的?我们打开一个rn页面,一般是通过scheme的形式打开,scheme的类似:qunariphone://react/open?hybridId=f_home_rn&pageName=Home&initProps=${encodeURIComponent ( JSON.stringify ({"param":{"cityName":"xx”,"bd_source":"xx”}}))}
,经过native代码桥接的QRCTJumpHandleManager
module配合动态路由,跳转到QReactNativeActivity
页面,再调用QReactHelper#doCreate
开始,创建rn环境。
qrn
对于ReactInstanceManager
的缓存处理
创建rn环境:
1.2 Image标签的渲染
以上react-native
环境是初始化完成,接下来分析一下,用react编写的Image
是如何渲染到页面上的。
一个简单的列子,
1 | import React, { Component } from 'react'; |
AppResgistry
注册完组件之后,Native端调用AppRegistry
的runApplication
开始渲染组件。
写过原生android的同学都知道,android一般用布局方式是LinearLayout
,RealativeLayout
,FrameLayout
等,并没有所谓的flex
布局,facebook
使用c++实现了一套flex
布局,即Yoga
.
在js端,facebook
实现了一套虚拟的dom树结构,可以在ReactNativeRender-prod.js
查看其实现方式,在ReactNativeRender
中,react-native
将所有的view操作都抽象为了UI操作
,对应的是native side
的UIOpeation
,比如:创建view,更新view,测量view。对应的是CreateViewOperation
,UpdateViewExtraData
,MeasureOperation
.
大致流程如下图所示:
React Component
在native端View的映射:
native和js端双边通信简易图:
参考文档
https://juejin.cn/post/6844903559280984071
https://yanbober.blog.csdn.net/article/details/53157456