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 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 additionalHttpHeaders) {
checkThread();
loadUrlImpl(url, additionalHttpHeaders);
}
private void loadUrlImpl(String url, Map 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 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 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开头的那些类代码,这个应该算是个动态控制的,不是固定坐标那种,有兴趣的可以看看。