public void onCreate(Bundle icicle) { super.onCreate(icicle); if (IntentHandler.handleWebSearchIntent(this, null, getIntent())) { finish(); return; } mController = new Controller(this, icicle == null); boolean xlarge = isTablet(this); if (xlarge) { mUi = new XLargeUi(this, mController); } else { mUi = new PhoneUi(this, mController); } mController.setUi(mUi); Bundle state = getIntent().getBundleExtra(EXTRA_STATE); if (state != null && icicle == null) { icicle = state; } mController.start(icicle, getIntent());上面的代码做了下面几件事: 1、 New了一个controller 对象,
2、根据当前设备是 phone还是pad 初始化了 I,
3. 把mUI传给 mController,让Controller 持有UI的引用以便后面可以操作 UI。
再稍微往下看两步,看看 controller和UI 里面有什么。Controller的构造函数 :
public Controller(Activity browser, boolean preloadCrashState) { mActivity = browser; mSettings = BrowserSettings.getInstance(); mTabControl = new TabControl(this); mSettings.setController(this); ……….省略了无关代码 mFactory = new BrowserWebViewFactory(browser); ……..省略了无关代码 }这里有一个 Tabcontrol,用来管理Tab ,这个Tab是什么呢?这要看下前面 UI这个接口的实现了,UI接口的实现类是 BaseUI,phoneUI 和XlargerUI均继承于 BaseUI(这里是典型的面向对象设计 )。BaseUI 由Titlebar和 Tab两部分组成,Titlebar就是我们看到网址输入那部分,而 Tab就是下面的网页部分,在 Tab.java的代码中,我们是可以找到 webview对象的,可以理解为Tab把 webview包装了一层。再回到controller这个类,注意 controller是实现了webviewCotroller ,UiController接口的(注意tabcontroller可不是接口,它只是一个普通类),到这里有没有感觉 controller.java这个类好强大,借助Tabcontroller管理着一组 Tab,Tab 有webview的实例, controller再根据具体的Tab对象控制着每一个 webview。这家伙就是browser app层的一个总管啊,只要能获得 controller的引用,就可以完成界面 webview的控制。 类的关系先说到这,下面还是看打开网页的流程吧,接 OnCeate()里面mController.start(icicle, getIntent()); 具体的代码如下
void start(final Bundle icicle, final Intent intent) { boolean noCrashRecovery = intent.getBooleanExtra(NO_CRASH_RECOVERY, false); if (icicle != null || noCrashRecovery) { doStart(icicle, intent, false); } else { mCrashRecoveryHandler.startRecovery(intent); } }不管走哪个分支,最后还是会来到同一个类中的 doStart(final Bundle icicle, final Intent intent, final boolean fromCrash),在 doStart()里面的代码是我们关心的。
GoogleAccountLogin.startLoginIfNeeded(mActivity, new Runnable() { @Override public void run() { onPreloginFinished(icicle, intent, currentTabId, restoreIncognitoTabs, fromCrash); } });这里开一个线程去开始load网页内容,线程 start动作在startLoginIfNeeded ()这里面。去 onPreloginFinished里面看看吧,
private void onPreloginFinished(Bundle icicle, Intent intent, long currentTabId, boolean restoreIncognitoTabs, boolean fromCrash) { //省略了暂时无关的代码 if (currentTabId == -1) { Tab t = null; if (urlData.isEmpty()) { t = openTabToHomePage();// 没有url就打开主页 } else { t = openTab(urlData);// 打开传过来的 url } } else { } }顺着上面的代码跟踪几步就会发现后面的流程大同小异,不管是 openTabToHomePage还是openTab ,都会执行 createNewTab(),再执行loadUrl() 函数,而 loadUrl()最终起作用代码在Tab.java,下面的代码
public void loadUrl(String url, Map<String, String> headers) { if (mMainView != null) { mPageLoadProgress = INITIAL_PROGRESS; mInPageLoad = true; mCurrentState = new PageState(mContext, false, url, null); mWebViewController.onPageStarted(this, mMainView, null); mMainView.loadUrl(url, headers); } }mMainView是 webview的实例,所以到这里packages层的代码流程也就结束了,开始转入 framework层,毫无疑问webview.java肯定是这一层的核心的代码之一了。 来看看看Webview里的loadUrl ()方法。
public void loadUrl(String url, Map<String, String> additionalHttpHeaders) { checkThread(); loadUrlImpl(url, additionalHttpHeaders); } 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);// 要注意这个 TAG。 clearHelpers(); }通过对上面TAG :EventHub.LOAD_URL的跟踪,可以发现代码已经来到 WebCoreView
case LOAD_URL: { CookieManager.getInstance().waitForCookieOperationsToComplete(); GetUrlData param = (GetUrlData) msg.obj; loadUrl(param.mUrl, param.mExtraHeaders); break; }那这里的loadUrl() 指向哪里呢,继续看很快可以找到,借助IDE工具理清这样的关系还是很快的。
private void loadUrl(String url, Map<String, String> extraHeaders) { if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url); mBrowserFrame.loadUrl(url, extraHeaders); }又出来个新变量mBrowserFrame,看下类型声明知道它是 BrowserFrame的实例,利用sourceInsight的跳转功能直接过去看看。
public void loadUrl(String url, Map<String, String> extraHeaders) { mLoadInitFromJava = true; if (URLUtil.isJavaScriptUrl(url)) { stringByEvaluatingJavaScriptFromString( url.substring("javascript:".length())); } else { nativeLoadUrl(url, extraHeaders); } mLoadInitFromJava = false; }这里看到nativeLoadUrl ,看来要用到 JNI的知识了,下面这句话告诉我们接下来去哪,
{ "nativeLoadUrl", "(Ljava/lang/String;Ljava/util/Map;)V", (void*) LoadUrl },WebCoreFrameBridge.cpp (external\webkit\source\webkit\android\jni)里的loadUrl 函数,到这里算是把请求送 c层的webkit 了,目前也只跟到这里,后面就是等回调消息,除了前面提到的类还会涉及到 CallBackproxy.java,这个家伙是一个非常重要的桥梁,我们的 BrowserFrame会通过CallbackProxy 向主线程发消息,总之它是比较重要的,对于webkit部分的代码理解目前还处在迷糊状态,不知道从哪里下手,所以这后面流程还有待进一步挖掘,同时求高手指点一二。
那么到这里小结一下,对 framework层的browser ,我们要重点关注 webview.java、CallBackproxy.java 、BrowserFrame.java、 WebCoreFrameBridge这几个类,其它类暂时还没注意到,肯定还有漏网之鱼,不过先把这几个主线类抓住再说,细节的东西慢慢来。
本文的故事也到此结束,要说明的上面的只是对android原生浏览器 java部分代码调用流程的简单分析,事实上应用层代码功能更偏向对网页( webview)的管理和显示,与webkit本身没有太多的联系,如果只是对 webkit感兴趣,只看下webkit部分是完全可以的,当然它的代码规模还比较大,要花些时间去理解。
PS:在目前的 browser设置中有个实验室选项,那个有快速控制选项,打开它会开启圆形的菜单功能,好像从android 3.0的版本开始就这个功能了,对应的代码实现是以 Pie开头的那些类代码,这个应该算是个动态控制的,不是固定坐标那种,有兴趣的可以看看。