浏览器探究——执行网页跳转
Main线程中执行。
UrlInputView
控件UrlInputView执行OnEditorAction->finishInput
/**
*url/search input view
*handling suggestions
*/
public class UrlInputView extendsAutoCompleteTextView
implements OnEditorActionListener,
CompletionListener, OnItemClickListener, TextWatcher
由此可见,UrlInputView即输入网址的那个控件,该控件包括输入URL和执行搜索输入。
Controller
在经过UrlInputView的处理后,会调用Controller.handleNewIntent。
Controller是浏览器中最重要的类,作为整个浏览器的主控类。Controller中有IntentHandler,这里会调用IntentHandler.onNewIntent
IntentHandler
//Handle all browser related intents
这里对一些特殊的URL做处理这里还包含了一些debug信息,比如”about:debug.dom.file"这种形式的网址。如果是普通的 url,不需要做特殊处理,则执行到最后执行Controller.loadUrlDataIn,即经过了IntentHandler后又回到了 Controller。
Controller
/**
* Load UrlData into a Tab and update the title bar to reflect the new
* load. Call this instead ofUrlData.loadIn directly.
* @param t The Tab used to load.
* @param data The UrlData being loaded.
*/
经过简单的处理,会把要加载的url作为参数调用被显示的tab,即执行Tab.loadUrl。这样看来Controller还只是个桥梁的作 用,把从UrlInputView得到的url,交给IntentHandler做处理,然后再把url交给Tab做实际的加载。
Tab
//Class for maintaining Tabs with a mainWebView and a subwindow.
Tab作为一个窗口的抽象概念而存在,对一个窗口的操作都需要通过Tab来进入。另外有个TabControl用来管理所有的Tab.
执行语句mMainView.loadUrl。这个mMainView是个WebView。从这终于进入了framework的webkit层了。
WebView
WebView作为Tab的核心控件,负责网页的真正的加载,显示等操作,是framework/webkit中最主要的类。通过Tab来找到WebView.
在WebView.loadUrl->WebView.loadUrlImpl中会向WebViewCore发消息,
WebViewCore
该类在一个独立的线程中运行,该类主要通过Message,Handler的机制在WebView与WebViewCore之间通讯的,这里 WebView是在Main线程中,而WebViewCore是在单独的 WebViewCoreThread线程中。WebViewCore是 framework/webkit核心类,它在单独的线程中来接收消息,每个消息相当于一个请求的任务,而这个WebViewCore相当于一个服务,它 在消息循环中不停的接收任务(消息),然后处理任务,然后再接收下一个任务(消息)。任务的执行很多是通过jni调用c层webkit来操作的。在c层 webkit有webkit/Source/WebKit/android/jni/WebViewCore.cpp与该java层的 WebViewCore对应。即这个WebViewCore是java层与c层的一个主要桥梁。
在WebView.loadUrlImpl在执行了mWebViewCore.sendMessage(EventHub.LOAD_URL, arg);则WebViewCore接收到LOAD_URL任务,执行对它的处理。
WebViewCore中有BrowserFrame成员
BrowserFrame
BrowserFrame extends Handler它也有很多JNI与C层的webkit的Webkit/Source/WebKit/android/jni /WebCoreFrameBridge.cpp对应。它用来处理页面的具体情况,即Frame,这个Frame是一个具体的页面的概念。
WebViewCore.loadUrl会调用BrowserFrame.loadUrl。BrowserFrame会调用到WebCoreFrameBridge.cpp的LoadUrl。
这里略去c层的处理部分。
WebCoreFrameBridge.cpp的LoadUrl执行后会执行到WebCoreFrameBridge.cpp的loadStarted,这里又会回调BrowserFrame.loadStarted函数。
/**
* native callback
* Indicates the beginning of a new load.
* This method will be called once for the main frame.
*/
即通过BrowserFrame的loadUrl在c层做处理后,又回调到BrowserFrame的loadStarted。
这里在理一下,主线程中有Tab,Tab里有WebView。即WebView也是运行在主线程的。而WebView里有WebViewCore,WebViewCore又是运行在一个单独的线程里的。
那么多个WebView是否就对应多个WebViewCore和多个WebViewCore线程呢?
看下WebView,WebView的构造函数中会mWebViewCore= new WebViewCore(context, this, mCallbackProxy, javaScriptInterfaces);创建了WebViewCore对象,即每个WebView都有个WebViewCore对象。但是 WebViewCore中的WebCoreThread是一个静态的,即一个单例的。而这个WebCoreThread中运行的 privatestatic Handler sWebCoreHandler;也是个静态的。
由此可见WebViewCore类本身就是个外壳,真正处理任务的是WebCoreThread及其里面的sWebCoreHandler,这个真 正处理任务的是静态的,只有一份,WebViewCore这个外壳只是沟通webView与WebCoreThread的桥梁,相当于一个代理类。是 WebCoreThread提供给外面的处理接口。
那么BrowserFrame呢?在WebViewCore的构造最后会发送一个INITIALIZE的消息给 WebCoreThread,WebCoreThread接收到后会执行WebViewCore.initialize,这个函数会构造 mrowserFrame。即每次构造WebViewCore时同样会构造一个BrowserFrame。但是为什么不在主线程中创建呢?一方面它是继承 自Handler,这个Handler需要在WebViewCoreThread线程中运行,所以需要在该线程来创建。另一方面在它的构造中需要创建 JWebCoreJavaBridge这个是C层的类,貌似这个类也需要在WebViewCoreThread线程中创建和销毁才行。
由上分析可知,WebView包含一个WebViewCore,WebViewCore包含一个BrowserFrame。多个 WebViewCore共同对应唯一的一个WebCoreThread。WebViewCore只是个外壳,WebCoreThread作为任务的处 理,WebCoreThread主要就是个大Handler不停的处理接收到的任务,WebCoreThread会调用很多jni来调用webkit接 口,BrowserFrame也有很多jni来调用webkit接口。由他们的组成情况又可知WebCoreThread直接调的jni是多个 WebView共同的,即不跟具体的WebView相关,而通过BrowserFrame调用的则跟一个具体的WebView相关了。
CallBackProxy
/**
*This class is a proxy class for handling WebCore -> UI thread messaging. All
*the callback functions are called from the WebCore thread and messages are
*posted to the UI thread for the actual client callback.
*/
/*
*This class is created in the UI thread so its handler and any private classes
*that extend Handler will operate in the UI thread.
*/
class CallbackProxy extends Handler {
另外还有个类CallBackProxy。它存在于WebView,WebViewCore,BrowserFrame中。在WebView构造函 数中创建,并在WebViewCore和BrowserFrame构造函数中作为参数传入并赋值给它们内部的引用。可见CallBackProxy是 WebViewCore和BrowserFrame向WebView回调的桥梁。
CallBackProxy是一个Handler,它在主线程中被创建,即WebViewCore和BrowserFrame会通过发消息的方式将 需求传递到主线程的CallBackProxy中,CallBackProxy的事件处理循环会执行相应的处理。可见主线程中WebView通过 WebViewCore发消息给WebViewCoreThread线程执行操作,WebViewCoreThread线程又会通过 CallBackProxy回调消息给主线程执行处理。
再回到BrowserFrame.loadStarted处。
BrowserFrame.loadStarted会调用 CallBackProxy.onPageStarted,CallBackProxy.onPageStarted中会给发PAGE_STARTED消 息,主线程中的CallBackProxy的handleMessage会执行处理。在处理时会执行 WebViewClient.onPageStarted。
WebViewClient
mWebViewClient本身只是提供一个接口基类,Tab中private finalWebViewClient mWebViewClient = new WebViewClient()
// WebViewClient implementation for themain WebView
//Set the WebViewClient that will receivevarious notifications and requests. This will replace the current handler.
这个WebViewClient会传给Tab中的WebView的CallbackProxy。这个WebViewClient会让CallbackProxy返回信息或回调处理给Tab。
回到WebViewClient.onPageStarted,这里做了一些判断处理后,调用WebViewController.onPageStarted。
WebViewController
//WebView aspect of the controller
public interface WebViewController
在Tab的构造时,WebViewController会作为参数传入,并设置给Tab。那么Tab中的WebViewController到底是什么?Tab是在TabController中创建的,而Tab构造时传入的是Controller。
public class Controller implementsWebViewController, UiController
由此可见Tab中的WebViewController其实就是对应Controller。只是通过WebViewController接口作为引用的。即Tab中只关心Controller的WebViewController接口部分。
这样就可以看出BrowserFrame通过消息与主线程中的CallbackProxy处理循环关联,CallbackProxy又通过 WebViewClient与Tab关联,回调了Tab提供的接口,而Tab又通过WebViewController接口,调用了Controller 这个核心类的函数。即调用到WebViewController.onPageStarted,也即是 Controller.onPageStarted.
Controller.onPageStarted中一个主要的处理是
if (!mNetworkHandler.isNetworkUp()) { view.setNetworkAvailable(false); }这里判断网络是否是激活状态,如果不是激活 状态则执行WebView.SetNetworkAvailable(Boolean networkUp)
/** * Inform WebView ofthe network state. This is used to set * the JavaScript property window.navigator.isOnline and * generates the online/offline event asspecified in HTML5, sec. 5.7.7 *@param networkUp boolean indicating if network is available */
该函数其实就是像WebViewCore发送SET_NETWORK_STATE消息,好吧,又回到了WebViewCoreThread线程了, 处理的方式是直接BrowserFrame.sJavaBridge.setNetworkOnLine。这个函数是个jni函数,其调用的是c层 JavaBridge的函数。这个jni函数主要就是通知c层WebCore网路状态已经变化,并把最新的网络状态告诉WebCore。
由此可见WebView.setNetworkAvailable还是个挺重要的函数,通过它才能告诉底层网络的状态。
那么额外看下网络状态如何获取并传递的,在loadUrl发起加载url这块判断了一次,即当无网络时主动通知一次。其他的呢?
在NetworkStateHandler中会注册一个BroadcastReceiver,该receiver会关注 ConnectivityManager.CONNECTIVITY_ACTION广播,该广播是在网络发生连接变化时被发出,该receiver在接收 到网络连接变化的广播后,会查询当前的连接情况,然后执行NetworkStarteHandler.onNetworkToggle,这个函数里面就会 调用WebView.setNetworkAvailable,也即跟上述描述的那样,给WebViewCoreThread线程发消息,通知c层 WebCore网络的最新状态。
这样就清楚了,网络广播时会通知C层最新状态,在执行loadUrl时,如果网络不可用也会通知C层网络最新状态。
这里先跨过Controller.onPageStarte的其他处理过程。此次任务处理完毕。
此时WebViewCoreThread线程中BrowserFrame仍然处于c层的LoadUrl中,此时的调用栈为:
BrowserFrame.nativeLoadUrl
BrowserFrame.loadUrl
WebViewCore.loadUrl
这里又会回调BrowserFrame.shouldInterceptRequest,它会调用BrowserFrame.inputStreamForAndroidResource函数
/**
* Get the InputStream for an Android resource
* There are three different kinds of android resources:
* - file:///android_res
* - file:///android_asset
* - content://
* @param url The url to load.
* @return An InputStream to the android resource
*/
这个函数会对几种特殊的url做不同的处理,当输入一个标准的http的url时,该函数不起什么作用。
BrowserFrame.shouldInterceptReques接下来会调用 CallbackProxy.shouldInterceptRequest,看到调用CallbackProxy的函数,基本上就是要发消息给主线程让 主线程去处理任务了,这里发送了LOAD_RESOURCE消息,主线程接收后执行WebViewClient.onLoadResource。回到 BrowserFrame.nativeLoadUrl处,此时这个jni的函数终于执行完毕,也不再回调其他函数了,该函数完 成,WebViewCoreThread线程执行完LoadUrl的处理。
当没有连接网络时,有个单独的线程会执行JWebCoreJavaBridge.signalServiceFuncPtrQueue这个JNI的 回调函数,该回调函数会发FYBCPTR_MESSAGE消息给WebViewCoreThread线程,但处理函数是 JWebCoreJavaBridge.handle,由此可见WebViewCoreThread线程不仅仅有一个Handler在处理事件.
JWebCoreJavaBridge
JWebCoreJavaBridge extends Handler 可见JWebCoreJavaBridge也是个Handler也是在WebViewCoreThread线程中运行的,只是发送消息的线程不是主线程, 是另一个单独的线程,并且消息的发送来源是JNI的回调。
JWebCoreJavaBridge对应的JNI是webkit/Source/WebKit/android/jni/JavaBridge.cpp的函数。
JWebCoreJavaBridge处理FYBCPTR_MESSAGE消息时会调用一个JNI函数,在JNI函数里又会回调BrowserFrame.reportError。
又是跟上面类似的流程,BrowserFrame.reportError调用CallbackProxy.onReceiveError,这个 CallbackProxy里会发消息REPORT_ERROR,然后主线程接收消息,执行 WebViewClient.onReceivedError。
接着又是收到FUNCPTR_MESSAGE消息,调用nativeServiceFuncPtrQueue,但是这里回调了 BrowserFrame.loadStarted,然后的处理流程跟上述的LoadUrl中调用BrowserFrame.loadStarted过程 一样。
接着WebViewCoreThread线程又是收到FUNCPTR_MESSAGE消息,调用 JWebCoreJavaBridge.nativeServiceFuncPtrQueue,WebViewCore.contentDraw被调用, 该函数发送WEBKIT_DRAW,处理该消息会执行绘制的操作。
接着WebViewCoreThread线程中JWebCoreJavaBridge.handleMessage又被调用,还是 FUNCPTR_MESSAGE消息,仍然是调用nativeServiceFuncPtrQueue。但此时BrowserFrame收到 loadFinished的回调,发PAGE_FINISHED消息,主线程接收消息处理时会执行WebView.onPageFinished和 WebViewClient.onFinished.