该文看下在ResourceLoadNotifier::didXXX中对页面加载进度的处理。
首先在ResourceLoadNotifier::didReceiveResponse中,通过Frame找到Page,然后通过Page找到ProgressTracker,然后
ProgressTracker
它负责计算和维护当前接收的进度情况,其中有成员
long longm_totalPageAndResourceBytesToLoad;
//这个记录了页面和资源的总大小,初始时用一个默认值static const int progressItemDefaultEstimatedLength = 1024 * 16;做设置。
long longm_totalBytesReceived;
double m_progressValue;
HashMap
//每个ResourceLoader有个唯一的identifier,这里对每个ResourceLoader创建一个ProgressItem,并用ResourceLoader的identifier作为key,来设置这个m_progressItemsMap映射。
intm_numProgressTrackedFrames; //该变量用于记录当前有几个Frame正在执行加载的操作。在progressStarted会加一,在progressCompleted时会减一,当它为0时即表示当前没有正在加载的Frame.
RefPtrm_originatingProgressFrame; //这个原始的ProgressFrame应该指最顶层的Frame,即Page的主Frame
Page中有成员ProgressTracker,并且是在Page的构造中创建的ProgressTracker成员。
这里要说下Page,Frame,FrameLoader的关系
一个Page对应浏览器的一个Tab页,就是说一个Page对应一个Tab下的完整的html网页
而一个html网页还可以有子Frame用iFrame来标识的,就像猫扑那样的页面,这样一个Page下就可以有多个Frame。其中有一个MainFrame。
一个Frame有一个FrameLoader。
由以上即可知道一个Page有一个ProgressTracker,而每个Frame会对应一个ProgressItem。
ProgressItem
这个类其实是个非常简单的数据结构的类,在ProgressTracker.cpp中被定义的。
long long bytesReceived;
long long estimatedLength; //注意这里只是估计的长度,初始时也是用上面的progressItemDefaultEstimatedLength给赋值的。
就这两个成员。
ProgressTracker的处理
这里主要关注几个函数
progressStarted //开始加载
incrementProgress //加载中
completeProgress //完成某个资源的加载
progressCompleted //一个Frame所有资源都加载完成后,做些结束处理。
接下来挨个看下这几个函数的情况。
ProgressTracker::progressStarted
在开始执行加载时被调用,看下调用栈:
#0 WebCore::ProgressTracker::progressStarted
#1WebCore::FrameLoader::prepareForLoadStart
#2WebCore::DocumentLoader::prepareForLoadStart
#3WebCore::FrameLoader::continueLoadAfterWillSubmitForm
#4WebCore::FrameLoader::continueLoadAfterNavigationPolicy
#5WebCore::FrameLoader::callContinueLoadAfterNavigationPolicy
#6WebCore::PolicyCallback::call
#7WebCore::PolicyChecker::continueAfterNavigationPolicy
#8 android::FrameLoaderClientAndroid::dispatchDecidePolicyForNavigationAction
#9 WebCore::PolicyChecker::checkNavigationPolicy
#10WebCore::FrameLoader::loadWithDocumentLoader
#11WebCore::FrameLoader::load
#12WebCore::FrameLoader::load
#13WebCore::FrameLoader::load
#14 LoadUrl
#15 dvmPlatformInvoke
可见在LoadUrl的调用链上发生的,由FrameLoader::prepareForLoadStart调用,即在真正开始发出http请求时调用。
这里会判断下当前是否没有正在加载的Frame,即判断m_numProgressTrackedFrames的值是否为0。或者判断当前的Frame是否是m_originatingProgressFrame,即是否是主Frame。如果是这两种的其中一种,会执行ProgressTracker::reset的操作,将所有成员清成初始值,然后设置一下配置,初始配置包括对m_originatingProgressFrame的设置和对m_progressValue的设置,m_progressValue的初始值是什么?初始没有进度的,这里设置成一个常量值,当前是0.1。之后会对m_numProgressTrackedFrames的值加一,表示已经开始有一个Frame执行加载的操作了。
在ProgressTracker的处理中大多数都是在自身做一些进度方面的计算后或者配置后,执FrameLoaderClientAndroid类的通知接口。典型的接口名为XXXNotification或者didXXX的形式。即通知和回调的接口。
那么ProgressTracker如何找到FrameLoaderClientAndroid呢,有二种途径。
第一种是通过参数传入的Frame。通过Frame找到FrameLoader,进一步找到FrameLoaderClient,在android平台上时FrameLoaderClientAndroid。
第二种通过成员m_originatingProgressFrame,这个成员本身是一个Frame的类型,按上述找到FrameLoaderClientAndroid。
FrameLoaderClientAndroid
该类是平台相关的FrameLoaderClient继承类,它有个重要的成员WebFrame,该WebFrame在android下是external/webkit/Source/WebKit/android/jni/WebCoreFrameBridge.h中定义的。这个WebFrame又有个成员JavaBrowserFrame,这个就对应了java层的BrowserFrame类,通过它可以和BrowserFrame通过jni相互调用。那么WebFrame其实就是对JavaBrowserFrame的包装,对外可以直接把WebFrame看成跟java层BrowserFrame的对应了。这样WebFrame提供的通知和回调接口,就可以发给java层的BrowserFrame了。
由上可知在ProgressTracker的处理中通过Frame找到了FrameLoaderClientAndroid,调用FrameLoaderClientAndroid的接口。FrameLoaderClientAndroid的接口中可以通过成员WebFrame来实现对java层的BrowserFrame的操作。这样流程就从平台无关的WebCore的ProgressTracker中通知到了平台相关的android的java层中了。
知道了这个流程后,会发现ProgressTracker的很多处理都是这样,就是自己做了计算后,通过上述流程调用到java层中的BrowserFrame中去。
ProgressTracker::incrementProgress
该函数有两种重载形式,一种是void ProgressTracker::incrementProgress(unsigned long identifier,const ResourceResponse& response)另一种是voidProgressTracker::incrementProgress(unsigned long identifier, const char*, intlength)
ProgressTracker::incrementProgress(unsignedlong identifier, const ResourceResponse& response)
当一个Frame接收到http的Response后会执行该重载函数,看下调用栈:
#0 WebCore::ProgressTracker::incrementProgress(this=0x11f6f18, identifier=44, response=...)
#1WebCore::ResourceLoadNotifier::didReceiveResponse
#2WebCore::ResourceLoader::didReceiveResponse
#3WebCore::MainResourceLoader::continueAfterContentPolicy
#4 WebCore::MainResourceLoader::continueAfterContentPolicy
#5WebCore::MainResourceLoader::callContinueAfterContentPolicy
#6 WebCore::PolicyCallback::call
#7WebCore::PolicyChecker::continueAfterContentPolicy
#8android::FrameLoaderClientAndroid::dispatchDecidePolicyForResponse
#9WebCore::PolicyChecker::checkContentPolicy
#10WebCore::MainResourceLoader::didReceiveResponse
#11WebCore::ResourceLoader::didReceiveResponse
#12android::WebUrlLoaderClient::didReceiveResponse
#13 DispatchToMethod
#14 RunnableMethod
#15 RunTask
首先从response中获取资源总长度信息作为该资源的估计长度,如果没有则用一个默认值来设置。然后把这个估计长度加入到m_totalPageAndResourceBytesToLoad,可见这个成员的值是不靠谱的值。
接下来会查询下m_progressItems中有没有跟参数传入的identifier对应的ProgressItem,如果有就获取它并重置下值,如果没有就新建一个ProgressItem并加入到m_progressItems映射中。注意每个ResourceLoader有一个identifier。则每个ResourceLoader对应一个ProgressItem。ProgressItem可以表明这个ResourceLoader估计要下载的数据量和已经下载的数据量。在这个处理中主要就是配置好该ResourceLoader对应的ProgressItem,然后等待接收数据了。
ProgressTracker::incrementProgress(unsignedlong identifier, const char*, int length)
当一个Frame接收到数据后会执行该重载函数,看下调用栈:
#0 WebCore::ProgressTracker::incrementProgress
#1WebCore::ResourceLoadNotifier::didReceiveData
#2WebCore::ResourceLoader::didReceiveData
#3WebCore::MainResourceLoader::didReceiveData
#4WebCore::ResourceLoader::didReceiveData
#5android::WebUrlLoaderClient::didReceiveData
#6 DispatchToMethod
#7 RunnableMethod
ProgressTracker::incrementProgres的参数有个unsignedlong identifie,用于告诉ProgressTracker是哪个ResourceLoader执行的,也即找到m_progressItems中对应的ProgressItem。
在ResourceLoadNotifier::didReceiveData中会将得到的数据长度作为参数传入。
ProgressTracker会利用这个长度来做一些计算,ProgressTracker的m_totalBytesReceived和ProgressItem的bytesReceived也会相应做增加,把新的数据长度加进来。
在ProgressTracker::incrementProgress中会通过Frame找到Frameloader,进一步找到FrameLoaderClient,这里是FrameLoaderClientAndroid。之后调用FrameLoaderClientAndroid::postProgressEstimateChangedNotification,在这里可以找到WebFrame,它是平台相关的类,可以在这里通过JNI调用到java层的代码处,在WebFrame中调用了WebFrame::SetProgress,把新的进度设置给java层。跟上述ProgressTracker::progressStarted的过程类似。
ProgressTracker::completeProgress
当一个Frame完成加载数据时,会执行该函数。看下调用栈:
#0 WebCore::ProgressTracker::completeProgress
#1WebCore::ResourceLoadNotifier::didFinishLoad
#2WebCore::ResourceLoader::didFinishLoadingOnePart
#3WebCore::ResourceLoader::didFinishLoading
#4WebCore::MainResourceLoader::didFinishLoading
#5WebCore::ResourceLoader::didFinishLoading
#6android::WebUrlLoaderClient::didFinishLoading
#7 DispatchToMethod
#8 RunnableMethod
#9 RunTask
该函数也会受到一个identifier的参数。这样通过m_progressItems找到该ResourceLoader对应的ProgressItem。
调整下估计值的情况,调整后,m_totalPageAndResourceBytesToLoad总算是靠谱了。
之后移除并delete掉这个ResourceLoader对应的ProgressItem,因为它已经没用了。
ProgressTracker::progressCompleted
一个Frame所有资源都加载完成后,做些结束处理。看下调用栈:
#0 WebCore::ProgressTracker::progressCompleted
#1WebCore::FrameLoader::checkLoadCompleteForThisFrame
#2WebCore::FrameLoader::recursiveCheckLoadComplete
#3WebCore::FrameLoader::checkLoadComplete
#4WebCore::DocumentLoader::removeSubresourceLoader
#5WebCore::SubresourceLoader::didFinishLoading
#6WebCore::ResourceLoader::didFinishLoading
#7android::WebUrlLoaderClient::didFinishLoading
#8 DispatchToMethod
#9 RunnableMethod
#10 RunTask
该Frame的所有资源都加载完了,则m_numProgressTrackedFrames计数减一。
之后做下判断m_numProgressTrackedFrames为0时则调用ProgressTracker::finalProgressComplete做最后的处理。ProgressTracker::finalProgressComplete函数的处理中又会调用ProgressTracker::reset做复位的操作,之后还是通知FrameLoaderClientAndroid做些回调处理。而ProgressTracker::progressCompleted的处理中也会发通知给FrameLoaderClientAndroid。
至此就完成了整个流程。
在以上的所有过程中FrameLoaderClientAndroid都可以通过m_frame->page()->progress()找到ProgressTracker,然后就可以读取ProgressTracker中计算的进度值情况了。
以上的处理中FrameLoaderClientAndroid主要应用的接口是
virtual void postProgressStartedNotification();
virtual voidpostProgressEstimateChangedNotification();
virtual voidpostProgressFinishedNotification();
这些接口的实现就不在细看了。