Browser浅析
纵观浏览器代码可知,Android WebView仍然是由Chromium驱动。与之前的浏览Android4.3源代码相比,可以看到之前版本中的external/WebKit目录被移除掉了,取而代之的是chromium_org。也就是说chromium已经完全取代了之前的WebKit for Android。虽然chromium完全取代了以前的WebKit for Android,但Android WebView的API接口并没有变,与老的版本完全兼容。这样带来的好处是基于WebView构建的APP,无需做任何修改,就能享受chromium内核的高效与强大。
当启动浏览器并加载一个url时的流程是如何的?基于这个目的分析android平台中browser代码来看下浏览器是如何启动并加载url的….
App层调用:
浏览器启动:
跟踪代码可知,浏览器启动时首先要加载一些全局的变量、默认属性、初始值、数据存储、数据传递、数据共享等信息。基于这个目的浏览器创建了自己的Application类---,而该类的基类为Application类。之所以使用基类Application是因为Application类是为了那些需要保存全局变量而设计的基本类。
当创建完自己的application类以后需要在中的application标签中进行设置,使用属性android:name=“Browser”注册自己的application类。此处是浏览器的application类-----类名为Browser。
当创建应用程序时OnCreate方法被调用,比应用程序中的其他对象创建的早。这个实现尽可能的快一点,因为在这个时间段内直接影响到应用程序中的第一个Activity / Service / Receiver的启动。而派生类需要复写这个OnCreate方法时必须在该方法中调用super.OnCreate方法并在这个方法中可以进行一些全局变量的初始化以及和上下文(context)有关系的变量和方法之类的信息数据等。
代码如下:
在上面这个应用程序类Browser中可以看到,该类继承于Application类并在派生类Browser中复写了OnCreate()方法。在该方法中调用了基类中的OnCreate方法,实例化了浏览器的CookieSyncManager管理、初始化了BrowserSettings的基本属性以及初始值、预加载了事件和创建了webviewFactory模式。
总之在Browser的OnCreate方法中加载了全局和上下文相关的类和方法。
当浏览器的application类Browser创建以后,需要在AndroidManifest.xml文件中进行设置,在标签 如下所示为浏览器的application名称:Browser 浏览器启动的第一个activity是那个呢?根据logcat中显示的信息可知,当启动浏览器时的第一个activity为BrowserActivity。如下: 浏览器加载URL: 由于浏览器启动时的第一个activity为BrowserActivity,该activity也就是展现给用户的第一个界面。众所周知,一个activity的启动首先执行的是OnCreate(…)函数,在这个函数中可以加载界面的布局文件、初始化相关的变量和方法等。 那么这个BrowserActivity启动时所执行的动作是什么呢?看下面代码: 在上面的OnCreate()函数中首先是判断用户是否对手持设备拥有了操作权限,如果没有操作权限则结束activity并返回return。其次是对搜索请求的处理,如果给定的intent(用getIntent()函数获取)的数据被认为是简单的搜索条件而不是URL或者快捷方式的话,则利用查询参数启动默认的web搜索活动。如果搜索请求被处理并且web搜索活动被启动则返回true,否则返回false。(BTW: IntentHandler类是处理所有浏览器相关的intent的类) 如果上述2种情况都不成立,则继续往下执行。对象mController指向了一个具体的实例,而这个具体实例是由createController()函数来创建的。继续跟踪代码并进入这个函数中查看具体实现,如下所示为createController()函数具体实现: 在这个函数中首先使用new Controller()生成了一个controller对象。之后调用isTablet()函数获得一个boolean值并把该值赋值给boolean变量xlarge。然后定义了一个UI ui变量,根据获取的xlarge的布尔值对当前设备进行了ui的初始化操作。对设备UI的初始化有2中ui方式:1,手持设备ui的初始化。2,非手持设备ui的初始化。因此上述代码中根据当前的设备进行不同ui的初始化。等ui初始化完成以后则调用controller的setUi(ui)方法把当前设备的ui和controller进行绑定。最后得到绑定当前设备ui的controller对象的实例并赋值给OnCreate()函数中的mController对象(在OnCreate函数中)。 总之,简而言之在这个函数当中是做了3件事情:1,New了一个controller 对象。2,根据当前设备是 phone还是非phone 初始化了 不同的UI。3,把UI传给 controller并绑定到controller上,让controller 持有UI的引用以便后面可以操作 UI。 接着再看一下createController()函数中的controller对象是如何创建的?代码中是使用new Controller(this)来创建controller对象的,在Controller()的构造函数中又执行了那些操作呢?继续查看代码并进入Controller()构造函数。该构造函数代码实现如下: 在Controller()的构造函数中得到BrowserSetting的实例,初始化了tab控制器,初始化了webview的工厂,创建了url处理类对象,创建了处理intent的对象,初始化了全局的handler,创建了bookmark的监听对象并复写了onChange(。。。)方法而且注册了该监听对象mBookmaksObserver。创建了网络状态的处理对象等信息。 通过Controller()构造函数初始化了这么多信息,那么怎样管理网页呢?这就是Tab控制器来管理Tab。 那么这个Tab又是什么呢?这要看下interface UI这个接口的实现类了,从代码中可以看到接口UI的实现类为BaseUI类,而phoneUI类和XlargerUI类都继承于BaseUI类。BaseUI类是由Titlebar和Tab两部分组成,Titlebar就是网址的输入部分,而Tab就是网址输入部分下面的网页部分。 如下是类的继承关系: 如下图一所示:红色部分为Titlebar,网页部分为Tab 通过跟踪代码可知,在BaseUI中包含titlebar部分和tab部分,那么这个tab部分的实现是什么呢?这就要看Tab.java源代码了,在这个源码中可以看到2个WebView和一个WebViewController的成员变量以及一个内部类PageState。PageState类是定义网页所需的所有状态类,是用来存储这个tab网页的一些信息,包括url、title、标题、图标等信息。而webview只是负责加载网页信息。 如下是包含的webview成员变量: 所包含的webviewcontroller变量: 内部类PageState 对Tab.Java中mContainer的赋值操作,这个赋值操作是由BaseUI中的onSetWebView(…)函数来实现的。在这个函数中进行了tab.xml的布局文件的加载并把加载的结果赋值给container变量,之后调用Tab.java中的方法setViewContainer(container)把得到的container数据赋值给Tab.java中的成员变量mContainer。这样Tab.java中的mContainer成员变量就成了Tab.java中的主UI了并可以进行后续的相关操作。 跟踪代码可知,该函数的调用是在Controller 中进行的,如下所示: 调用顺序为:Controller:setActiveTab--->TabControl:setCurrentTab--->Tab:setWebView--->Controller:onSetWebView--->BaseUI:onSetWebView,之后继续执行代码 并调用该接口的实现类BaseUI中的setActiveTab进行处理,最终是调用attachTabToContentView来处理tab的webview也就是mainView并添加到container中,之后把该container和mContentView关联起来了。 至此就完成了Tab的container和mContentView之间的联系了。现在要返回到Controller.java文件中,看一下这个Controller的类的继承关系: Controller类中实现了WebViewController、UiController、ActivityController等的接口.如下所示为Controller的继承关系: 通过上面的分析和跟踪代码可知,Controller类借助Tabcontrol管理着一组 Tab,Tab 有webview的实例, 之后Controller再根据具体的Tab对象控制着每一个 webview。只要能获得 Controller的引用,就可以完成界面 webview的控制。 到这一步为止,只是得到了Controller的一个实例并且这个实例赋值给了BrowserActivity类中OnCreate()函数中的mController对象。 跟踪代码并返回到BrowserActivity类的OnCreate()函数中,继续执行代码使用条件表达式获取Intent对象。之后把获取的Intent对象作为参数执行Start(intent)动作并调用Controller类中的Start(Intent)函数完成加载的动作。继续跟踪代码,函数调用关系为:Start---> mCrashRecoveryHandler.startRecovery(intent);---doStart(…)如下代码: 在这个函数中首先处理了以下浏览器崩溃时所发生的动作,最终调用的是doStart()函数来处理当前加载网页的动作。 继续跟踪代码,在startLoginIfNeeded()函数中执行了start动作,而在onPreloginFinished()函数中进行了url的加载。如下所示: 在onPreloginFinished()函数中,根据获取的urlData数据并判断urlData数据是否有效。如果urlData为空则调用openTabToHomePage()函数完成打开主页的动作。如果urlData不为空则调用openTab(url)函数以urlData为参数进行指定url的加载动作。 继续跟踪代码,不管是 openTabToHomePage(…)函数还是openTab(…)函数 ,都会执行 createNewTab()函数并创建一个新的tab页,然后再执行loadUrlDataIn(…) 函数进行当前tab页的url加载动作。如下所示: 而 loadUrlDataIn(…)最终起作用的代码在Tab.java文件中,下面是代码调用关系: Controller:loadUrlDataIn--->Controller:loadUrl。如下所示: 由以上可知,在Controller的loadurl(…)函数中调用了tab.loadUrl(…)函数,而tab.loadUrl(…)函数是在Tab.java文件中定义的。因此继续跟踪代码并进入Tab.java文件中的loadUrl()函数中看执行了那些动作。 Tab.java文件中的loadUrl()函数: 在上面的loadUrl()函数中创建了一个PageState的对象mCurrentState,执行了Controller文件中的onPageStarted()函数加载一个新的页面,最后调用mMainView..loadUrl(…)函数来加载指定的url。根据mMainView类型可知,loadUrl(。。。)为WebView.java文件中的函数并且此处被调用。 所以到这里packages层的app代码流程也就结束了,开始转入 framework层。 Framework层调用: 在framework层的WebView.java是这一层的核心的代码之一,根据app层中的分析结果继续跟踪代码并进入WebView.java文件的loadUrl()函数中,如下所示描述: 在这个函数中调用了mProvider.loadUrl(…)函数,根据mProvider类型可知,调用了类中的loadUrl(。。。)函数。继续查看代码可知,在这个类中的函数调用顺序为:loadUrl(…)--->loadUrlOnUiThread(…)。而在loadUrlOnUiThread(…)函数中调用了mAwContents . loadUrl(…)函数。继续深入代码,最终是调用的是ContentViewCore类中的loadUrl(…)函数。 如下所示: 根据上面的函数描述可知,至此为止使用nativeLoadUrl(..)函数把请求的url送到了C层的webkit中进行处理。之后就是等待返回消息。 ///由此framework层java代码已经调用完毕。JNI层对应C或者C++的映射没有继续跟踪。