android webkit学习笔记1---url的load过程

webkit总体上分成两块,核心库,android适配层。


下面通过webkit打开baidu来分析下具体的过程,就可以知道webkit的工作模式了。


1. 首先是app中创建了webView,并调用它的loadurl方法:

     

mWebView.loadUrl("http://www.baidu.com");

2.   frameworks/base/core/java/android/webkit/webView.java

      

    public void loadUrl(String url) {
        checkThread();
        loadUrlImpl(url);
    }

       继续去调他的实现:

     

    private void loadUrlImpl(String url) {
        if (url == null) {
            return;
        }
        loadUrlImpl(url, null);
    }
    重载的函数:

    private void loadUrlImpl(String url, Map<String, String> extraHeaders) {
        switchOutDrawHistory();
        WebViewCore.GetUrlData arg = new WebViewCore.GetUrlData();
        arg.mUrl = url;
        arg.mExtraHeaders = extraHeaders;
        mWebViewCore.sendMessage(EventHub.LOAD_URL, arg);
        clearHelpers();
    }

这里出现了新的对象,mWebViewCore,是在WebView初始化的时候创建的,WebViewCore主要负责和Core(核心库)的交流。

实际接收这个事件的是webViewCore的内部成员EventHub,其实从发送事件的类型大概也可以才出来。

3. frameworks/base/core/java/android/webkit/WebViewCore.java

 EventHub.transferMessages()方法

        private void transferMessages() {
//略
                        case LOAD_URL: {
                            CookieManager.getInstance().waitForCookieOperationsToComplete();
                            GetUrlData param = (GetUrlData) msg.obj;
                            loadUrl(param.mUrl, param.mExtraHeaders);
                            break;
                        }
//略
}

    private void loadUrl(String url, Map<String, String> extraHeaders) {
        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
        mBrowserFrame.loadUrl(url, extraHeaders);
    }

调用browserFrame的loadurl方法,在webkit中,每一个网页都被称为一个frame。

4. frameworks/base/core/java/android/webkit/BrowserFrame.java

    public void loadUrl(String url, Map<String, String> extraHeaders) {
        mLoadInitFromJava = true;
        if (URLUtil.isJavaScriptUrl(url)) {
            // strip off the scheme and evaluate the string
            stringByEvaluatingJavaScriptFromString(
                    url.substring("javascript:".length()));
        } else {
            nativeLoadUrl(url, extraHeaders);
        }
        mLoadInitFromJava = false;
    }

  调用JNI的方法:

5. external/webkit/Source/WebKit/android/jni/WebCoreFrameBridge.cpp

   j

static void LoadUrl(JNIEnv *env, jobject obj, jstring url, jobject headers)
{
//...
    pFrame->loader()->load(request, false);
}

首先将传入的url打包到KURL中,再用KURL构造request。

这里的pFrame->loader()得到FrameLoader,再调用它的load方法:

6.  external/webkit/Source/WebKit/WebCore/loader/FrameLoader.cpp

void FrameLoader::load(const ResourceRequest& request, bool lockHistory)
{
    load(request, SubstituteData(), lockHistory);
}

void FrameLoader::load(const ResourceRequest& request, const SubstituteData& substituteData, bool lockHistory)
{
    if (m_inStopAllLoaders)
        return;
        
    // FIXME: is this the right place to reset loadType? Perhaps this should be done after loading is finished or aborted.
    m_loadType = FrameLoadTypeStandard;
    RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, substituteData);
    if (lockHistory && m_documentLoader)
        loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory().string() : m_documentLoader->clientRedirectSourceForHistory());
    load(loader.get());
}

在这一步里创建了DocumentLoader,并将其传入方法load中:

void FrameLoader::load(DocumentLoader* newDocumentLoader)
{
    ResourceRequest& r = newDocumentLoader->request();
    addExtraFieldsToMainResourceRequest(r);
    FrameLoadType type;

    if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->originalRequest().url())) {
        r.setCachePolicy(ReloadIgnoringCacheData);
        type = FrameLoadTypeSame;
    } else
        type = FrameLoadTypeStandard;

    if (m_documentLoader)
        newDocumentLoader->setOverrideEncoding(m_documentLoader->overrideEncoding());
    
    if (shouldReloadToHandleUnreachableURL(newDocumentLoader)) {
        // shouldReloadToHandleUnreachableURL() returns true only when the original load type is back-forward.
        // In this case we should save the document state now. Otherwise the state can be lost because load type is
        // changed and updateForBackForwardNavigation() will not be called when loading is committed.
        history()->saveDocumentAndScrollState();

        ASSERT(type == FrameLoadTypeStandard);
        type = FrameLoadTypeReload;
    }

    loadWithDocumentLoader(newDocumentLoader, type, 0);
}


调用loadWithDocumentLoader

void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState)
{
    // Retain because dispatchBeforeLoadEvent may release the last reference to it.
    RefPtr<Frame> protect(m_frame);

    ASSERT(m_client->hasWebView());

    // Unfortunately the view must be non-nil, this is ultimately due
    // to parser requiring a FrameView.  We should fix this dependency.

    ASSERT(m_frame->view());

    if (m_pageDismissalEventBeingDispatched)
        return;

    if (m_frame->document())
        m_previousUrl = m_frame->document()->url();
    policyChecker()->setLoadType(type);
    RefPtr<FormState> formState = prpFormState;
    bool isFormSubmission = formState;

    const KURL& newURL = loader->request().url();
    const String& httpMethod = loader->request().httpMethod();

    if (shouldScrollToAnchor(isFormSubmission,  httpMethod, policyChecker()->loadType(), newURL)) {
        RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader;
        NavigationAction action(newURL, policyChecker()->loadType(), isFormSubmission);

        oldDocumentLoader->setTriggeringAction(action);
        policyChecker()->stopCheck();
        policyChecker()->checkNavigationPolicy(loader->request(), oldDocumentLoader.get(), formState,
            callContinueFragmentScrollAfterNavigationPolicy, this);//注意这里传入的函数指针
    } else {
        if (Frame* parent = m_frame->tree()->parent())
            loader->setOverrideEncoding(parent->loader()->documentLoader()->overrideEncoding());

        policyChecker()->stopCheck();
        setPolicyDocumentLoader(loader);
        if (loader->triggeringAction().isEmpty())
            loader->setTriggeringAction(NavigationAction(newURL, policyChecker()->loadType(), isFormSubmission));

        if (Element* ownerElement = m_frame->ownerElement()) {
            if (!ownerElement->dispatchBeforeLoadEvent(loader->request().url().string())) {
                continueLoadAfterNavigationPolicy(loader->request(), formState, false);
                return;
            }
        }

        policyChecker()->checkNavigationPolicy(loader->request(), loader, formState,
            callContinueLoadAfterNavigationPolicy, this);
    }
}


调用策略检查,并将回调函数作为参数传入:

7. external/webkit/Source/WebKit/WebCore/loader/PolicyCheker.cpp

void PolicyChecker::checkNavigationPolicy(const ResourceRequest& request, DocumentLoader* loader,
    PassRefPtr<FormState> formState, NavigationPolicyDecisionFunction function, void* argument)
{
    NavigationAction action = loader->triggeringAction();
    if (action.isEmpty()) {
        action = NavigationAction(request.url(), NavigationTypeOther);
        loader->setTriggeringAction(action);
    }
    if (equalIgnoringHeaderFields(request, loader->lastCheckedRequest()) || (!request.isNull() && request.url().isEmpty())) {
        function(argument, request, 0, true);
        loader->setLastCheckedRequest(request);
        return;
    }
    
    // We are always willing to show alternate content for unreachable URLs;
    // treat it like a reload so it maintains the right state for b/f list.
    if (loader->substituteData().isValid() && !loader->substituteData().failingURL().isEmpty()) {
        if (isBackForwardLoadType(m_loadType))
            m_loadType = FrameLoadTypeReload;
        function(argument, request, 0, true);
        return;
    }
    loader->setLastCheckedRequest(request);

    m_callback.set(request, formState.get(), function, argument);//将传入的callback赋值给m_callback.m_navigationFunction

    m_delegateIsDecidingNavigationPolicy = true;
    m_frame->loader()->client()->dispatchDecidePolicyForNavigationAction(&PolicyChecker::continueAfterNavigationPolicy,
        action, request, formState);//将policychecker的continu函数传入,等待client回调
    m_delegateIsDecidingNavigationPolicy = false;
}


调用client的的policycheck,这里的client即是FrameLoaderClientAndroid

8.external/webkit/Source/WebKit/android/support/FrameLoaderClientAndroid.cpp

void FrameLoaderClientAndroid::dispatchDecidePolicyForNewWindowAction(FramePolicyFunction func,
                                const NavigationAction& action, const ResourceRequest& request,
                                PassRefPtr<FormState> formState, const String& frameName) {
    ASSERT(m_frame);
    ASSERT(func);
    if (!func)
        return;
    if (request.isNull()) {
        (m_frame->loader()->policyChecker()->*func)(PolicyIgnore);
        return;
    }

    if (action.type() == NavigationTypeFormSubmitted || action.type() == NavigationTypeFormResubmitted)
        m_frame->loader()->resetMultipleFormSubmissionProtection();

    // If we get to this point it means that a link has a target that was not
    // found by the frame tree. Instead of creating a new frame, return the
    // current frame in dispatchCreatePage.
    if (canHandleRequest(request))
        (m_frame->loader()->policyChecker()->*func)(PolicyUse);
    else
        (m_frame->loader()->policyChecker()->*func)(PolicyIgnore);
}

9. 回调policychecker:

external/webkit/Source/WebKit/WebCore/loader/PolicyCheker.cpp

void PolicyChecker::continueAfterNewWindowPolicy(PolicyAction policy)
{
    PolicyCallback callback = m_callback;
    m_callback.clear();

    switch (policy) {
        case PolicyIgnore:
            callback.clearRequest();
            break;
        case PolicyDownload:
            m_frame->loader()->client()->startDownload(callback.request());
            callback.clearRequest();
            break;
        case PolicyUse:
            break;
    }

    callback.call(policy == PolicyUse);//调用之前传入的函数指针
}



void PolicyCallback::call(bool shouldContinue)
{
    if (m_navigationFunction)
        m_navigationFunction(m_argument, m_request, m_formState.get(), shouldContinue);
    if (m_newWindowFunction)
        m_newWindowFunction(m_argument, m_request, m_formState.get(), m_frameName, m_navigationAction, shouldContinue);
    ASSERT(!m_contentFunction);
}

m_navigationFuction就是:FrameLoader::callContinueLoadAfterNavigationPolicy

 10.external/webkit/Source/WebKit/WebCore/loader/FrameLoader.cpp

void FrameLoader::callContinueLoadAfterNavigationPolicy(void* argument,
    const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue)
{
    FrameLoader* loader = static_cast<FrameLoader*>(argument);
    loader->continueLoadAfterNavigationPolicy(request, formState, shouldContinue);
}

这里的loader就是当初传入的this,所以还是FrameLoader。

void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, PassRefPtr<FormState> formState, bool shouldContinue)
{
//。。。
        continueLoadAfterWillSubmitForm();
}

void FrameLoader::continueLoadAfterWillSubmitForm()
{
    if (!m_provisionalDocumentLoader->startLoadingMainResource(identifier))
        m_provisionalDocumentLoader->updateLoading();
}

调用documentloader,去loadmainres。

11. external/webkit/Source/WebKit/WebCore/loader/DocumentLoader.cpp

bool DocumentLoader::startLoadingMainResource(unsigned long identifier)
{
    ASSERT(!m_mainResourceLoader);
    m_mainResourceLoader = MainResourceLoader::create(m_frame);
    m_mainResourceLoader->setIdentifier(identifier);

    // FIXME: Is there any way the extra fields could have not been added by now?
    // If not, it would be great to remove this line of code.
    frameLoader()->addExtraFieldsToMainResourceRequest(m_request);

    if (!m_mainResourceLoader->load(m_request, m_substituteData)) {
        // FIXME: If this should really be caught, we should just ASSERT this doesn't happen;
        // should it be caught by other parts of WebKit or other parts of the app?
        LOG_ERROR("could not create WebResourceHandle for URL %s -- should be caught by policy handler level", m_request.url().string().ascii().data());
        m_mainResourceLoader = 0;
        return false;
    }

    return true;
}

创建了mainresourceloader,并调用它的load函数。


12. external/webkit/Source/WebKit/WebCore/loader/MainResourceLoader.cpp

     

bool MainResourceLoader::load(const ResourceRequest& r, const SubstituteData& substituteData)
{
    ASSERT(!m_handle);

    m_substituteData = substituteData;

    ASSERT(documentLoader()->timing()->navigationStart);
    ASSERT(!documentLoader()->timing()->fetchStart);
    documentLoader()->timing()->fetchStart = currentTime();
    ResourceRequest request(r);

#if ENABLE(OFFLINE_WEB_APPLICATIONS)
    documentLoader()->applicationCacheHost()->maybeLoadMainResource(request, m_substituteData);
#endif
    bool defer = defersLoading();
    if (defer) {
        bool shouldLoadEmpty = shouldLoadAsEmptyDocument(request.url());
        if (shouldLoadEmpty)
            defer = false;
    }
    if (!defer) {
        if (loadNow(request)) {
            // Started as an empty document, but was redirected to something non-empty.
            ASSERT(defersLoading());
            defer = true;
        }
    }
    if (defer)
        m_initialRequest = request;

    return true;
}

bool MainResourceLoader::loadNow(ResourceRequest& r)
{
    bool shouldLoadEmptyBeforeRedirect = shouldLoadAsEmptyDocument(r.url());

    ASSERT(!m_handle);
    ASSERT(shouldLoadEmptyBeforeRedirect || !defersLoading());

    // Send this synthetic delegate callback since clients expect it, and
    // we no longer send the callback from within NSURLConnection for
    // initial requests.
    willSendRequest(r, ResourceResponse());

    // <rdar://problem/4801066>
    // willSendRequest() is liable to make the call to frameLoader() return NULL, so we need to check that here
    if (!frameLoader())
        return false;
    const KURL& url = r.url();
    bool shouldLoadEmpty = shouldLoadAsEmptyDocument(url) && !m_substituteData.isValid();

    if (shouldLoadEmptyBeforeRedirect && !shouldLoadEmpty && defersLoading())
        return true;

    resourceLoadScheduler()->addMainResourceLoad(this);
    if (m_substituteData.isValid()) 
        handleDataLoadSoon(r);
    else if (shouldLoadEmpty || frameLoader()->representationExistsForURLScheme(url.protocol()))
        handleEmptyLoad(url, !shouldLoadEmpty);
    else
        m_handle = ResourceHandle::create(m_frame->loader()->networkingContext(), r, this, false, true);

    return false;
}


这里最关键的一步是创建了ResourceHandle,

13 external/webkit/Source/WebCore/platform/network/ResourceHandle.cpp

PassRefPtr<ResourceHandle> ResourceHandle::create(NetworkingContext* context, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff)
{
#if ENABLE(BLOB)
    if (request.url().protocolIs("blob")) {
        PassRefPtr<ResourceHandle> handle = blobRegistry().createResourceHandle(request, client);
        if (handle)
            return handle;
    }
#endif

    RefPtr<ResourceHandle> newHandle(adoptRef(new ResourceHandle(request, client, defersLoading, shouldContentSniff)));

    if (newHandle->d->m_scheduledFailureType != NoFailure)
        return newHandle.release();

    if (newHandle->start(context))
        return newHandle.release();

    return 0;
}

对于Android来说,这类的start是ResourceLoadererAndroid的静态方法start:

14 . external/webkit/Source/WebCore/platform/network/android/ResourceHandleAndroid.cpp

bool ResourceHandle::start(NetworkingContext* context)
{
    MainResourceLoader* mainLoader = context->mainResourceLoader();
    bool isMainResource = static_cast<void*>(mainLoader) == static_cast<void*>(client());
    RefPtr<ResourceLoaderAndroid> loader = ResourceLoaderAndroid::start(this, d->m_firstRequest, context->frameLoaderClient(), isMainResource, false);

    if (loader) {
        d->m_loader = loader.release();
        return true;
    }

    return false;
}

15 external/webkit/Source/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp

PassRefPtr<ResourceLoaderAndroid> ResourceLoaderAndroid::start(
        ResourceHandle* handle, const ResourceRequest& request, FrameLoaderClient* client, bool isMainResource, bool isSync)
{
    // Called on main thread
    FrameLoaderClientAndroid* clientAndroid = static_cast<FrameLoaderClientAndroid*>(client);
#if USE(CHROME_NETWORK_STACK)
    WebViewCore* webViewCore = WebViewCore::getWebViewCore(clientAndroid->getFrame()->view());
    bool isMainFrame = !(clientAndroid->getFrame()->tree() && clientAndroid->getFrame()->tree()->parent());
    return WebUrlLoader::start(client, handle, request, isMainResource, isMainFrame, isSync, webViewCore->webRequestContext());
#else
    return clientAndroid->webFrame()->startLoadingResource(handle, request, isMainResource, isSync);
#endif
}

这里有个宏,如果开启这个宏,就使用external/ chromium/下的库,如果没有,就使用apache-http。

4.0上这个宏是开启的,我们继续往下看。

首先是获取了clientAndroid,这个是上一步中的NetworkContext通过frameLoaderClient()来获取的。

而接下来通过clientAndroid获取到了WebViewCore,并得到了WebrequestContext,这个对象中存放了HTTP相关的环境设置,如UA,cache,语言等。

最后,将一系列的参数传入:

WebUrlLoader::start(client, handle, request, isMainResource, isMainFrame, isSync, webViewCore->webRequestContext());

client是frameLoaderClient,提供平台相关的信息,handle是ResourceHandle,request里封装了URL,webrequestContext里封装了环境设置。

接下来就是通过WebUrlloader来获取资源了。

WebUrlloader继承于ResourceLoaderAndroid

15. external/webkit/Source/WebKit/android/WebCoreSupport/WebUrlLoader.cpp

PassRefPtr<WebUrlLoader> WebUrlLoader::start(FrameLoaderClient* client, WebCore::ResourceHandle* resourceHandle,
        const WebCore::ResourceRequest& resourceRequest, bool isMainResource, bool isMainFrame, bool isSync, WebRequestContext* context)
{
    FrameLoaderClientAndroid* androidClient = static_cast<FrameLoaderClientAndroid*>(client);
    WebFrame* webFrame = androidClient->webFrame();

    webFrame->maybeSavePassword(androidClient->getFrame(), resourceRequest);

    RefPtr<WebUrlLoader> loader = WebUrlLoader::create(webFrame, resourceHandle, resourceRequest);
    loader->m_loaderClient->start(isMainResource, isMainFrame, isSync, context);

    return loader.release();
}
没干啥事,把活丢给了m_loaderClient。

16.external/webkit/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp

bool WebUrlLoaderClient::start(bool isMainResource, bool isMainFrame, bool sync, WebRequestContext* context)
{
    base::Thread* thread = ioThread();
    if (!thread) {
        return false;
    }

    m_isMainResource = isMainResource;
    m_isMainFrame = isMainFrame;
    m_sync = sync;
    if (m_sync) {
        AutoLock autoLock(*syncLock());
        m_request->setSync(sync);
        m_request->setRequestContext(context);
        thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::start));

        // Run callbacks until the queue is exhausted and m_finished is true.
        // Sometimes, a sync load can wait forever and lock up the WebCore thread,
        // here we use TimedWait() with multiple tries to avoid locking.
        const int kMaxNumTimeout = 3;
        const int kCallbackWaitingTime = 10;
        int num_timeout = 0;
        while(!m_finished) {
            while (!m_queue.empty()) {
                OwnPtr<Task> task(m_queue.front());
                m_queue.pop_front();
                task->Run();
            }
            if (m_finished) break;

            syncCondition()->TimedWait(base::TimeDelta::FromSeconds(kCallbackWaitingTime));
            if (m_queue.empty()) {
                LOGE("Synchronous request timed out after %d seconds for the %dth try, URL: %s",
                     kCallbackWaitingTime, num_timeout, m_request->getUrl().c_str());
                num_timeout++;
                if (num_timeout >= kMaxNumTimeout) {
                    cancel();
                    m_resourceHandle = 0;
                    return false;
                }
            }
        }

        // This may be the last reference to us, so we may be deleted now.
        // Don't access any more member variables after releasing this reference.
        m_resourceHandle = 0;
    } else {
        // Asynchronous start.
        // Important to set this before the thread starts so it has a reference and can't be deleted
        // before the task starts running on the IO thread.
        m_request->setRequestContext(context);
        thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::start));
    }
    return true;
}

我们这里看异步的过程。

首先是创建了个线程(叫network,是个静态的,第一次会创建,后面就直接返回了),然后给线程丢了个tast,让他去调用m_request的start方法。

到这里就返回了,我们理一下到目前的调用栈的状况:



MessageLoop::PostTask

WebUrlLoaderClient::start()

WebUrlLoader::start()

ResourceLoaderAndroid::start()

ResourceHandle::start()

ResourceHandle::create()

MainResourceLoader::loadNow()

MainResourceLoader::load()

DocumentLoader::startLoadingMainResource()

FrameLoader::continueLoadAfterWillSubmitForm()

FrameLoader::continueLoadAfterNavigationPolicy()

FrameLoader::callContinueLoadAfterNavigationPolicy()

PolicyCallback::call()

PolicyChecker::continueAfterNavigationPolicy()

FrameLoaderClientAndroid::dispatchDecidePolicyForNavigationAction()

PolicyChecker::checkNavigationPolicy()

FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState)

FrameLoader::load(DocumentLoader* newDocumentLoader)

FrameLoader::load()

FrameLoader::load()

android:WebCoreFrameBridge::LoadUrl()

android:BrwoserFrame.nativeLoadUrl()

android:BrowserFrame.loadUrl()

android:WebVIewCore.loadUrl()

android:WebViewCore.EventHub.transferMessages()

android:WebView:loadUrlImpl()

android:WebView.loadUrlImpl()

android::WebView.loadUrl()


还是比较长的,其中关键的线就是url数据的打包和传递。


接下来看看request的实现:

假设现在MessageLoop调用了我们的WebRequest的start函数:

1. external/webkit/Source/WebKit/android/WebCoreSupport/WebRequest.cpp




  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值