前言
在浏览器里面输入网址,最终浏览器会调用WebView的loadUrl(),然后就开始加载整个网页。整个加载过程中,最重要的一步就是HTML主资源的加载。WebKit将网页的资源分为主资源(MainResource)和子资源(SubResource)。
WebKit资源分类
主资源:HTML文件。
子资源:CSS, JS, JPG等等,除了HTML文件之外的所有资源都称之为子资源
本章主要讲主资源的加载过程,子资源的加载过程后期会专门详细的分析和讲解。
主资源请求
LoadUrl
主资源的请求是从WebView的loadUrl开始的。根据之前《Android WebKit消息处理》的讲解,WebView的操作都会有WebViewClassic进行代理。资源加载肯定是由WebCore来处理的,所以,WebVewClassic会发消息给WebViewCore,让WebViewCore最终将loadUrl传递给C++层的WebKit处理:
01.
/**
02.
* See {@link WebView#loadUrl(String, Map)}
03.
*/
04.
@Override
05.
public
void
loadUrl(String url, Map<String, String> additionalHttpHeaders) {
06.
loadUrlImpl(url, additionalHttpHeaders);
07.
}
08.
09.
private
void
loadUrlImpl(String url, Map<String, String> extraHeaders) {
10.
switchOutDrawHistory();
11.
WebViewCore.GetUrlData arg =
new
WebViewCore.GetUrlData();
12.
arg.mUrl = url;
13.
arg.mExtraHeaders = extraHeaders;
14.
mWebViewCore.sendMessage(EventHub.LOAD_URL, arg);
15.
clearHelpers();
16.
}
WebViewCore在接收到LOAD_URL之后,会通过BrowserFrame调用nativeLoadUrl,这个BrowserFrame与C++层的mainFrame对接。这里顺便提一下clearHeapers()的作用:如果当前网页有对话框dialog,有输入法之类的,clearHelpers就是用来清理这些东西的。这也是为什么加载一个新页面的时候,但当前页面的输入法以及dialog消失等等。WebViewCore收到消息之后,会直接让BrowserFrame调用JNI: nativeLoadUrl():
01.
// BrowserFrame.java
02.
public
void
loadUrl(String url, Map<String, String> extraHeaders) {
03.
mLoadInitFromJava =
true
;
04.
if
(URLUtil.isJavaScriptUrl(url)) {
05.
// strip off the scheme and evaluate the string
06.
stringByEvaluatingJavaScriptFromString(
07.
url.substring(
"javascript:"
.length()));
08.
}
else
{
09.
/** M: add log */
10.
Xlog.d(XLOGTAG,
"browser frame loadUrl: "
+ url);
11.
nativeLoadUrl(url, extraHeaders);
12.
}
13.
mLoadInitFromJava =
false
;
14.
}
01.
// WebCoreFrameBridge.cpp
02.
static
void
LoadUrl(JNIEnv *env, jobject obj, jstring url, jobject headers)
03.
{
04.
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
05.
ALOG_ASSERT(pFrame,
"nativeLoadUrl must take a valid frame pointer!"
);
06.
07.
WTF::String webcoreUrl = jstringToWtfString(env, url);
08.
WebCore::KURL kurl(WebCore::KURL(), webcoreUrl);
09.
WebCore::ResourceRequest request(kurl);
10.
if
(headers) {
11.
// dalvikvm will raise exception if any of these fail
12.
jclass mapClass = env->FindClass(
"java/util/Map"
);
13.
jmethodID entrySet = env->GetMethodID(mapClass,
"entrySet"
,
14.
"()Ljava/util/Set;"
);
15.
jobject set = env->CallObjectMethod(headers, entrySet);
16.
17.
jclass setClass = env->FindClass(
"java/util/Set"
);
18.
jmethodID iterator = env->GetMethodID(setClass,
"iterator"
,
19.
"()Ljava/util/Iterator;"
);
20.
jobject iter = env->CallObjectMethod(set, iterator);
21.
22.
jclass iteratorClass = env->FindClass(
"java/util/Iterator"
);
23.
jmethodID hasNext = env->GetMethodID(iteratorClass,
"hasNext"
,
"()Z"
);
24.
jmethodID next = env->GetMethodID(iteratorClass,
"next"
,
25.
"()Ljava/lang/Object;"
);
26.
jclass entryClass = env->FindClass(
"java/util/Map$Entry"
);
27.
jmethodID getKey = env->GetMethodID(entryClass,
"getKey"
,
28.
"()Ljava/lang/Object;"
);
29.
jmethodID getValue = env->GetMethodID(entryClass,
"getValue"
,
30.
"()Ljava/lang/Object;"
);
31.
32.
while
(env->CallBooleanMethod(iter, hasNext)) {
33.
jobject entry = env->CallObjectMethod(iter, next);
34.
jstring key = (jstring) env->CallObjectMethod(entry, getKey);
35.
jstring value = (jstring) env->CallObjectMethod(entry, getValue);
36.
request.setHTTPHeaderField(jstringToWtfString(env, key), jstringToWtfString(env, value));
37.
env->DeleteLocalRef(entry);
38.
env->DeleteLocalRef(key);
39.
env->DeleteLocalRef(value);
40.
}
41.
// ...
42.
pFrame->loader()->load(request,
false
);
43.
}
接下来,在JNI的LoadUrl中就开始创建ResourceRequest,由于WebView的java层面可以对url的请求头进行设定,然后通过FrameLoader进行加载。这里的pFrame就是与Java层的BrowserFrame对应的mainFrame。HTML在WebKit的层次上看,最低层的是Frame,然后才有Document,也就意味着HTML Document也是通过Frame的FrameLoader加载的:
1.
pFrame->loader()->load(request,
false
);
调用栈
最后的这句话就是让FrameLoader去加载url的request。后面的调用栈依次是:1.
void
FrameLoader::load(
const
ResourceRequest& request, bool lockHistory)
2.
void
FrameLoader::load(
const
ResourceRequest& request,
const
SubstituteData& substituteData, bool lockHistory)
3.
void
FrameLoader::load(DocumentLoader* newDocumentLoader)
4.
void
FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState)
5.
void
FrameLoader::callContinueLoadAfterNavigationPolicy(
void
* argument,
6.
const
ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue)
7.
void
FrameLoader::continueLoadAfterNavigationPolicy(
const
ResourceRequest&, PassRefPtr<FormState> formState, bool shouldContinue)
8.
void
FrameLoader::continueLoadAfterWillSubmitForm()
01.
void
FrameLoader::load(
const
ResourceRequest& request,
const
SubstituteData& substituteData, bool lockHistory)
02.
{
03.
if
(m_inStopAllLoaders)
04.
return
;
05.
06.
// FIXME: is this the right place to reset loadType? Perhaps this should be done after loading is finished or aborted.
07.
m_loadType = FrameLoadTypeStandard;
08.
RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, substituteData);
09.
if
(lockHistory && m_documentLoader)
10.
loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory().string() : m_documentLoader->clientRedirectSourceForHistory());
11.
load(loader.get());
12.
}
01.
// FrameLoader.cpp
02.
void
FrameLoader::continueLoadAfterWillSubmitForm()
03.
{
04.
// ...
05.
m_provisionalDocumentLoader->timing()->navigationStart = currentTime();
06.
07.
// ...
08.
if
(!m_provisionalDocumentLoader->startLoadingMainResource(identifier))
09.
m_provisionalDocumentLoader->updateLoading();
10.
}
三种DocumentLoader
这里需要对m_provisionalDocumentLoader进行讲解下:
1.
RefPtr<DocumentLoader> m_documentLoader;
2.
RefPtr<DocumentLoader> m_provisionalDocumentLoader;
3.
RefPtr<DocumentLoader> m_policyDocumentLoader;
4.
void
setDocumentLoader(DocumentLoader*);
5.
void
setPolicyDocumentLoader(DocumentLoader*);
6.
void
setProvisionalDocumentLoader(DocumentLoader*);
01.
// FrameLoader.cpp
02.
void
FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState)
03.
{
04.
// ...
05.
policyChecker()->stopCheck();
06.
// ...
07.
setPolicyDocumentLoader(loader);
08.
// ..
09.
}
10.
11.
void
FrameLoader::continueLoadAfterNavigationPolicy(
const
ResourceRequest&, PassRefPtr<FormState> formState, bool shouldContinue)
12.
{
13.
// ...
14.
setProvisionalDocumentLoader(m_policyDocumentLoader.get());
15.
m_loadType = type;
16.
setState(FrameStateProvisional);
17.
// ...
18.
setPolicyDocumentLoader(
0
);
19.
20.
}
21.
22.
void
FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage)
23.
{
24.
// ...
25.
setDocumentLoader(m_provisionalDocumentLoader.get());
26.
setProvisionalDocumentLoader(
0
);
27.
// ...
28.
}
29.
30.
void
FrameLoader::checkLoadCompleteForThisFrame()
31.
{
32.
switch
(m_state) {
33.
case
FrameStateProvisional: {
34.
// ...
35.
36.
// If we're in the middle of loading multipart data, we need to restore the document loader.
37.
if
(isReplacing() && !m_documentLoader.get())
38.
setDocumentLoader(m_provisionalDocumentLoader.get());
39.
40.
// Finish resetting the load state, but only if another load hasn't been started by the
41.
// delegate callback.
42.
if
(pdl == m_provisionalDocumentLoader)
43.
clearProvisionalLoad();
44.
45.
}
46.
47.
// ...
48.
}
01.
// FrameLoader.cpp
02.
void
FrameLoader::recursiveCheckLoadComplete()
03.
{
04.
Vector<RefPtr<Frame>,
10
> frames;
05.
06.
for
(RefPtr<Frame> frame = m_frame->tree()->firstChild(); frame; frame = frame->tree()->nextSibling())
07.
frames.append(frame);
08.
09.
unsigned size = frames.size();
10.
for
(unsigned i =
0
; i < size; i++)
11.
frames[i]->loader()->recursiveCheckLoadComplete();
12.
13.
checkLoadCompleteForThisFrame();
14.
}
15.
16.
// Called every time a resource is completely loaded, or an error is received.
17.
void
FrameLoader::checkLoadComplete()
18.
{
19.
ASSERT(m_client->hasWebView());
20.
21.
m_shouldCallCheckLoadComplete =
false
;
22.
23.
// FIXME: Always traversing the entire frame tree is a bit inefficient, but
24.
// is currently needed in order to null out the previous history item for all frames.
25.
if
(Page* page = m_frame->page())
26.
page->mainFrame()->loader()->recursiveCheckLoadComplete();
27.
}
startLoadingMainResource
在m_provisionalDocumentLoader调用startLoadingMainResource之后,就开始准备发送网络请求了。调用栈如下:01.
bool DocumentLoader::startLoadingMainResource(unsigned
long
identifier)
02.
bool MainResourceLoader::load(
const
ResourceRequest& r,
const
SubstituteData& substituteData)
03.
bool MainResourceLoader::loadNow(ResourceRequest& r)
04.
PassRefPtr<ResourceHandle> ResourceHandle::create(NetworkingContext* context,
05.
const
ResourceRequest& request,
06.
ResourceHandleClient* client,
07.
bool defersLoading,
08.
bool shouldContentSniff)
09.
bool ResourceHandle::start(NetworkingContext* context)
10.
PassRefPtr<ResourceLoaderAndroid> ResourceLoaderAndroid::start(
11.
ResourceHandle* handle,
const
ResourceRequest& request,
12.
FrameLoaderClient* client, bool isMainResource, bool isSync)
13.
bool WebUrlLoaderClient::start(bool isMainResource, bool isMainFrame, bool sync, WebRequestContext* context)
网络数据
Android WebKit数据下载在Chromium_net的IO线程中完成之后会通过WebUrlLoaderClient向WebCore提交数据。WebKt的调用栈如下:
01.
// Finish
02.
void
WebUrlLoaderClient::didFinishLoading()
03.
void
ResourceLoader::didFinishLoading(ResourceHandle*,
double
finishTime)
04.
void
MainResourceLoader::didFinishLoading(
double
finishTime)
05.
void
FrameLoader::finishedLoading()
06.
void
DocumentLoader::finishedLoading()
07.
void
FrameLoader::finishedLoadingDocument(DocumentLoader* loader)
08.
void
FrameLoaderClientAndroid::finishedLoading(DocumentLoader* docLoader)
09.
void
FrameLoaderClientAndroid::committedLoad(DocumentLoader* loader,
10.
const
char
* data,
int
length)
11.
void
DocumentLoader::commitData(
const
char
* bytes,
int
length)
12.
13.
14.
15.
16.
// Receive Data
17.
void
WebUrlLoaderClient::didReceiveData(scoped_refptr<net::IOBuffer> buf,
int
size)
18.
void
ResourceLoader::didReceiveData(ResourceHandle*,
const
char
* data,
int
length,
19.
int
encodedDataLength)
20.
void
ResourceLoader::didReceiveData(
const
char
* data,
int
length,
21.
long
long
encodedDataLength, bool allAtOnce)
22.
void
MainResourceLoader::addData(
const
char
* data,
int
length, bool allAtOnce)
23.
void
DocumentLoader::receivedData(
const
char
* data,
int
length)
24.
void
DocumentLoader::commitLoad(
const
char
* data,
int
length)
25.
void
FrameLoaderClientAndroid::committedLoad(DocumentLoader* loader,
26.
const
char
* data,
int
length)
27.
void
DocumentLoader::commitData(
const
char
* bytes,
int
length)
这个过程其实分为两步,一步是Chromium_net收到数据,另一部是Chromium_net通知WebKit,数据已经下载完毕可以finish了。这个两个过程都会调用FrameLoaderClienetAndroid::committedLoad()。只不过参数不一样,在finish的时候,将传入的length为0,这样通知WebKit,数据已经传送完毕,记者WebKit就开始使用commitData拿到的数据进行解析,构建Dom Tree和Render Tree。关于Dom Tree Render Tree的构建过程下一节详细的讲述。