1.Flutter系统网络加载流程
Flutter系统网络加载流程
Flutter原生支持在Image组件上显示网络,源码最简单的详解使用方式如下,调用Image的源码命名构造方法Image.network即可实现网络的下载显示。Widgetimage=Image.network(imageUrl);那么,详解它内部是源码如何实现的呢?是否有做缓存处理或其他优化操作呢?带着疑问,我们一起来看下它的详解keil源码插入断点底层究竟是如何实现的。
一、源码从构造函数开始我们以最简单的详解调用方式举例,当我们使用Image.network(imageUrl)这种方式来显示时,源码Image组件内部image属性就会被赋值NetworkImage。详解
//此为简化过的源码Image组件类结构classImageextendsStatefulWidget{ Image.network(Stringsrc,):image=NetworkImage(src);//数据处理的基类finalImageProviderimage;}这里引出了一个类叫NetworkImage,它是详解ImageProvider的子类,专门实现网络的源码下载和解析逻辑。当然你直接点进去看到的详解其实是个抽象类,并不是源码真正实现下载逻辑的地方,真正实现网络下载解析的在'_network_image_io.dart’这个文件下。构造函数知道这些就够了。element源码下载接下来就看Image是在何时触发网络的下载的。
二、下载入口Image是一个StatefulWidget,它又一个对应的State叫_ImageState。在这个_ImageState的生命周期中,控制着的下载过程。
State的生命周期可以简单的分为:构造函数→initState→didChangeDependencies→build
因此,我们顺着这个顺序找,很快看到一个可疑的地方,didChangeDependencies中的_resolveImage方法。而TickerMode则是用于控制动画的,在这里被用于判断是否禁用了动画。关于TickerMode的相关介绍,可以看下这篇文章
//完整源码@overridevoiddidChangeDependencies(){ _updateInvertColors();//处理的入口_resolveImage();//当动画被禁用时,也是无法显示的,这个if(TickerMode.of(context))//添加流处理的containerd 源码解析监听_listenToStream();else_stopListeningToStream(keepStreamAlive:true);super.didChangeDependencies();}我们进入到_resolveImage方法中去。
void_resolveImage(){ //ScrollAwareImageProvider包装了我们的NetworkImagefinalScrollAwareImageProviderprovider=ScrollAwareImageProvider<Object>(context:_scrollAwareContext,imageProvider:widget.image,);//新建流finalImageStreamnewStream=provider.resolve(createLocalImageConfiguration(context,size:widget.width!=null&&widget.height!=null?Size(widget.width!,widget.height!):null,));assert(newStream!=null);//更新流_updateSourceStream(newStream);}_resolveImage方法就做了三件事。
1、用ScrollAwareImageProvider包装了NetworkImage
2、创建流对象ImageStream
3、更新流
2.1、ScrollAwareImageProviderScrollAwareImageProvider也是ImageProvider的子类,它的作用很简单,就是防止在快速滑动的时候加载,当存在快速滑动时,会将解析的工作放到下一帧处理。至于具体如何实现,我们放在后面再提。
2.2、ImageConfigurationImageConfiguration由方法createLocalImageConfiguration创建,保存了的基本配置信息,如Bundle,白银模式源码屏幕项目比devicePixelRatio,本地化local,尺寸size,平台platform等。
2.3、ImageStream表示一个流,可以添加观察者ImageStreamCompleter来监听是否处理完成。一个流可以添加多个观察者。
ImageStream由provider的resolve方法调用后创建。通过源码可知,此处的provider就是ScrollAwareImageProvider对象。但是它内部并没有实现resolve方法,因此此处调用的是父类ImageProvider的resolve方法。
三、流和Key以下代码截取自ImageProvider,并且删减了无关代码。查看svn源码
ImageStreamresolve(ImageConfigurationconfiguration){ //创建流,这里直接调用了ImageStream的构造函数,并没有用到configurationfinalImageStreamstream=createStream(configuration);//关键在这里,这里会根据configuration创建一个唯一key_createErrorHandlerAndKey(configuration,//成功的回调(Tkey,ImageErrorListenererrorHandler){ resolveStreamForKey(configuration,stream,key,errorHandler);},//下面是错误回调,可以不关注(T?key,Objectexception,StackTrace?stack)async{ awaitnull;//waitaneventturnincasealistenerhasbeenaddedtotheimagestream.InformationCollector?collector;if(stream.completer==null){ stream.setCompleter(_ErrorImageCompleter());}stream.completer!.reportError(exception:exception,stack:stack,context:ErrorDescription('whileresolvinganimage'),silent:true,//couldbeanetworkerrororwhatnotinformationCollector:collector,);},);returnstream;}resolve方法的作用是创建流对象ImageStream,并根据传入的配置信息configuration,创建对应的Key,这个Key用于缓存。
那么这个key到底是怎么创建的呢,我们进入到_createErrorHandlerAndKey方法中查看。关键代码如下,已删除无关代码。
Future<T>key;try{ key=obtainKey(configuration);}catch(error,stackTrace){ handleError(error,stackTrace);return;}key.then<void>((Tkey){ obtainedKey=key;try{ successCallback(key,handleError);}catch(error,stackTrace){ handleError(error,stackTrace);}}).catchError(handleError);可以看到方法实现中调用了ImageProvider的obtainKey方法,而这个方法在ImageProvider并没有具体实现,需要子类完成对应的实现。
Future<T>obtainKey(ImageConfigurationconfiguration);还记得上文的分析不,我们说传入的imageProvider实例是ScrollAwareImageProvider对象,因此对应的实现也要到这个类中去查找。很快,我们找到obtainKey方法的实现,可以看到它做了个透传,具体是由它包装的类也就是NetworkImage来实现的。
@overrideFuture<T>obtainKey(ImageConfigurationconfiguration)=>imageProvider.obtainKey(configuration);那么,我们就去NetworkImage找obtainKey。
注意下真正的NetworkImage实现是在_network_image_io.dart文件下的。
Future<NetworkImage>obtainKey(image_provider.ImageConfigurationconfiguration){ returnSynchronousFuture<NetworkImage>(this);}到这,我们就知道了NetworkImage的key为SynchronousFuture。
获取到key后的下一步就是调用_createErrorHandlerAndKey方法的successCallback回调。从而触发了下一个流程resolveStreamForKey。
_createErrorHandlerAndKey(configuration,(Tkey,ImageErrorListenererrorHandler){ //拿到Key之后的回调resolveStreamForKey(configuration,stream,key,errorHandler);})四、根据key来处理流还是回到子类ScrollAwareImageProvider中,它重写了父类的resolveStreamForKey方法,前文提到,ScrollAwareImageProvider是用来防止列表在快速滑动的时候来加载的,那么它是如何实现的?我们就从resolveStreamForKey这个方法中来一探究竟。
//此为简化过的Image组件类结构classImageextendsStatefulWidget{ Image.network(Stringsrc,):image=NetworkImage(src);//数据处理的基类finalImageProviderimage;}0Scrollable用于滑动组件,它有个方法叫recommendDeferredLoadingForContext,表示是否建议延迟加载。内部最终是根据滑动速度和当前设备的最大物理尺寸的边去比较,如果大于,表示速度过快,那么就建议延迟。具体逻辑在scroll_physics.dart文件下。这里不多做介绍。
一旦当前应用处于滑动状态,并且速度过快,那么,的加载将会被推迟到下一帧再进行尝试。因此我们说,当处于快速滑动时,是无法加载的。
当判断可以加载时,操作流将会被移交给被包装类imageProvider,这里是NetworkImage来处理。但是,NetworkImage没有实现resolveStreamForKey方法,因此最终还是跑到了ImageProvider类中的resolveStreamForKey方法下。
//此为简化过的Image组件类结构classImageextendsStatefulWidget{ Image.network(Stringsrc,):image=NetworkImage(src);//数据处理的基类finalImageProviderimage;}1当第一次加载网络图的时候,会直接走到下面这个逻辑中。这里涉及到一个很重要的类,ImageCache。它是做缓存用的。
//此为简化过的Image组件类结构classImageextendsStatefulWidget{ Image.network(Stringsrc,):image=NetworkImage(src);//数据处理的基类finalImageProviderimage;}.1、ImageCache缓存类,只做了内存缓存。它由PaintingBinding持有,是一个单利。它的内部通过三个Map来缓存。
//此为简化过的Image组件类结构classImageextendsStatefulWidget{ Image.network(Stringsrc,):image=NetworkImage(src);//数据处理的基类finalImageProviderimage;}3从缓存器中获取的逻辑集中在putIfAbsent方法中。以下代码已经去掉无关代码。
//此为简化过的Image组件类结构classImageextendsStatefulWidget{ Image.network(Stringsrc,):image=NetworkImage(src);//数据处理的基类finalImageProviderimage;}.2、load一旦在ImageCache中找不到缓存的,就会通过loader回调出来,走真正的下载流程。
//此为简化过的Image组件类结构classImageextendsStatefulWidget{ Image.network(Stringsrc,):image=NetworkImage(src);//数据处理的基类finalImageProviderimage;}5还是先看ScrollAwareImageProvider类,里面实现了load方法,并透传给了NetworkImage来实现。
//此为简化过的Image组件类结构classImageextendsStatefulWidget{ Image.network(Stringsrc,):image=NetworkImage(src);//数据处理的基类finalImageProviderimage;}6在NetworkImage下,可以找到对应的load方法实现。里面有个_loadAsync方法,它就是我们要找的下载核心代码。
//此为简化过的Image组件类结构classImageextendsStatefulWidget{ Image.network(Stringsrc,):image=NetworkImage(src);//数据处理的基类finalImageProviderimage;}7五、下载饶了一大圈,终于来到了下载的地方了。可以看到下载的逻辑很简单,创建一个下载的/post/