VasSonic是腾讯推出的为了提升H5页面首屏加载速度而推出的高性能Hybrid框架,目前普遍应用在QQ商城等Hybrid界面中,以提升用户体验。javascript
https://github.com/Tencent/Va... GitHub地址html
几乎全部的Hybrid界面都以WebView界面为载体,H5界面加载的时间主要消耗在在WebView初始化、网络请求、WebView渲染三个部分。WebView初始化与WebView渲染均是100ms的时间量级,其中最主要的时间瓶颈在网络请求上,尤为是弱网状况下,其消耗时间将达到s级,致使H5界面白屏时间较长或没法正常打开。
为了解决这个问题,WebView中提供了public WebResourceResponse shouldInterceptRequest(WebView view, String url)
函数负责拦截并加载html中用到的资源文件,以此为基础,引出VasSonic的核心思想并行加载,本地缓存,模板变动。
总体的思路即是利用WebView初始化的时间并行的进行网络请求,并利用缓存进行预加载,网络请求完成后再把变化的部分返回给浏览器利用JS进行数据变动。java
整个源码咱们应以SonicSession文件为切入点,它管理整个并行流程,负责获取缓存,网络请求,并以向外暴露抽象方法的方式交给子类处理错误处理,预加载,模板变动,并提供与JS的惟一交互出口,下面咱们具体走一遍它的流程。
当WebView启动初始化前,便会调用SonicSession的start方法,而后会在子线程调用runSonicFlow方法,开始并行处理。git
在runSonicFlow中,咱们会尝试获取缓存,并交由子类的handleLocalHtml进行处理,预加载就能够在这里处理。github
然后会运行handleFlow_connection方法,首先他会获取SessionConnect,以后会添加本地缓存的一些信息从而获取变化后的网页,进行connect操做后,获取到responseCode,会交给SonicSession的子类进行hanldeFlow_304,handleFlow_HttpError,handleFlow_ServiceUnavailable一系列错误处理。
以后会判断缓存是否为空,缓存为空的回话会交由子类的handleFlow_FirstLoad处理,不为空的话回判断是Template变化的话会交由子类的handleFlow_TemplateChange处理,Data变化的话会交由子类的handleFlow_DataChange处理。json
可见,SonicSession主要向子类在几个关键的节点上暴露方法,获取缓存后子类调用handleLocalHtml处理,网络链接结束后会根据错误码等信息交由子类的hanldeFlow_304, andleFlow_HttpError, handleFlow_ServiceUnavailable 进行一系列错误处理,获取网络请求成功后,若是缓存为空,会调用子类的handleFlowFirstLoad处理,缓存不为空的话,会根据结果的header信息判断是模板变化仍是数据变化,分别交由handleFlow_TemplateChange,handleFlow_DataChange处理。跨域
在SonicSession中,一样会提供一些方法用于各类事件的回调。
SessionClient建立完成后调用OnClientReady方法,这个方法标识着WebView已经初始化完成。
当WebView开始加载资源后,会被shouldInterceptRequest方法拦截,会调用咱们的OnClientRequestResource方法,标识着WebView开始加载数据。浏览器
当WebView渲染完成,会调用OnWebReady方法,其中会携带着JS的回调方法,最后会经过setResult调用H5的回调方法。缓存
由上面对SonicSession的分析可知,子类SonicSession所须要关注的点有
调用loadUrl或loadUrlWithBaseUrl启动浏览器加载流程。
赋值pengdingWebResourceStream,并返回给浏览器解析。
模板和Data变化通知给浏览器作相应处理。
拦截资源实现加载。
调用setResult进行浏览器回调。安全
1.StandardSonicSession
基于安全性上的考量,StandardSonicSession模式下仅支持loadUrl。
在浏览器准备完成后,咱们就能够直接调用loadUrl,因此在OnClientReady中,咱们直接就能够调用loadUrl。
关于pendingWebResourceStream的赋值,在上面一条线的流程中完成。
WebView支持边加载边渲染的特性,咱们能够将流传进去后,继续进行写操做,因而定义了SonicSessionStream,以后咱们会介绍到。
因此须要判断数据是否接受完成,对赋值操做作不一样的处理,而后再作保存数据的操做。
下面咱们看一下StandardSession的流程到底是怎么处理的,哪些地方对pendingWebResourceStream进行了操做。
在上面的流程图中能够看到会根据模板与Data的变化进行不一样的处理。
由上面的分析可知WebView在loadUrl后,可在onClientRequestResource方法中对浏览器资源的加载进行拦截,注意!这个方法内只会拦截咱们的html文件,进行文件流的传入,资源文件并不会拦截。这个方法作了对网络流的等待,等待pengdingWebResourceStream有值以后,就会将流返回给浏览器进行加载渲染。
setResul在onWebReady中需吊用一次,在这里咱们肯定回调方法。
之后每次流程发生转变的时候需调用一次,能够帮咱们明确每一次请求走的流程,更主要的是当Data变化时,咱们能够调用浏览器的回调方法。
2.QuickSonicSession
与Standard核心的不一样点便在于能够经过loadBaseUrl实现加在缓存的html字符串,速度上有必定优点,但安全性上有必定问题,经实测速度优点不明显。
在实现上,和Standard模式咱们须要关注变化的点主要就是不能在OnClientReady中不能直接调用loadUrl,须要在有缓存的时候loadBaseUrl,而后本身构造header,其余变化不大。
下面咱们梳理下具体的流程,首先咱们展示的是初始化的流程。
下面展示的是SonicSession start后的流程。
不一样线程间等待如何调度与通讯
网络请求所在的业务在Sonic子线程中,主要经过主线程的handler与WebView所在的主线程通讯。
WebView所在的主线程主要经过控制Atomic变量对子线程进行控制。
当WebView加载完成,网络请求的结果还未赋值时,将经过同步锁的方式等待网络请求的结果。
其实WebView是支持边加载边渲染的特性的,只要将数据流传递给WebView便可,因而提供了SonicSessionStream支持这种特性,下面会介绍到。
如何作网络流和内存流的桥接
能够看到网络数据完成时,直接将数据放入Stream中,而未完成时,则建立了SonicSessionStream。
其中上面的outputStream表明着已读取到内存中的memStream,responseStream则表明着仍未读取的netStream。
经过重写SonicSeesionStream的read方法便可实现桥接。
如何作局部刷新
当缓存数据网页加载完成,即调用onWebReady后,会经过javascriptInterface将js的回调方法返回给App。
当服务器想通知客户端局部刷新时,会经过头部的template-tag通知,并返回data的json。
经过比较本地的data与新data返回差别data,并将结果赋值给pendingDiffData。
而后会经过setResult调用js的回调方法,完成局部刷新过程。
缓存文件储存
进行缓存时,会计算返回结果的sha1值,并存入sp中。
获取缓存时,会从sp中读取文件的sha1值进行比对。
head管理(跨域问题)