从之前的文章中我们可以看到, BrowserActivity 是浏览器的核心Activity了, 是浏览器的入口, 但是他里面并没有处理很多复杂的逻辑, 只是实现一些android
先看看时序图
系统对activity的回调. 这些逻辑交给了Controller来处理, 那我们首先先入门一下, 一步一步的来看看浏览器是怎么从启动到打开Tab的 吧
从入口 BrowserActivity的 onCreate 函数开始:
02 | public void onCreate(Bundle icicle) { |
05 | mController = new Controller(this, icicle == null); |
06 | boolean xlarge = isTablet(this); |
09 | mUi = new XLargeUi(this, mController); |
11 | mUi = new PhoneUi(this, mController); |
14 | mController.setUi(mUi); |
C mController = new Controller(this, icicle == null);
Controller这个类,这是浏览器的核心, 其他我们暂时忽略这里只看TabControl的初始化:
1 | public Controller(Activity browser, boolean preloadCrashState) { |
3 | mTabControl = new TabControl(this); //初始化tab的控制器 |
mTabControl = new TabControl(this); //初始化tab的控制器, TabControl是管理所有Tab的Controller , 将来添加Tab添加到这个 链表中.
然后 , 如果是手机就会执行 pad版本大同小异就不做介绍了!
mUi = new PhoneUi(this, mController); 这句话, 代码如下
05 | public PhoneUi(Activity browser, UiController controller) { |
06 | super(browser, controller); |
07 | setUseQuickControls(BrowserSettings.getInstance().useQuickControls()); //设置快速控制菜单,就是那个piemenu |
08 | mNavigationBar = (NavigationBarPhone) mTitleBar.getNavigationBar(); |
09 | TypedValue heightValue = new TypedValue(); |
10 | browser.getTheme().resolveAttribute( |
11 | com.android.internal.R.attr.actionBarSize, heightValue, true); |
12 | mActionBarHeight = TypedValue.complexToDimensionPixelSize(heightValue.data, |
13 | browser.getResources().getDisplayMetrics()); |
首先会调用BaseUI的构造函数 这里会执行一系列的View的初始化,这里传入了activity对象,所以可以设置activity的各种UI
01 | public BaseUi(Activity browser, UiController controller) { |
03 | mUiController = controller; |
04 | mTabControl = controller.getTabControl(); |
05 | Resources res = mActivity.getResources(); |
06 | mInputManager = (InputMethodManager) |
07 | browser.getSystemService(Activity.INPUT_METHOD_SERVICE); |
08 | mLockIconSecure = res.getDrawable(R.drawable.ic_secure_holo_dark); |
09 | mLockIconMixed = res.getDrawable(R.drawable.ic_secure_partial_holo_dark); |
10 | FrameLayout frameLayout = (FrameLayout) mActivity.getWindow() |
11 | .getDecorView().findViewById(android.R.id.content);//拿到activity的content 然后把view加到一个layout上 |
12 | LayoutInflater.from(mActivity) |
13 | .inflate(R.layout.custom_screen, frameLayout); //居然用这种方式这是activity的view! |
14 | mContentView = (FrameLayout) frameLayout.findViewById( |
16 | mCustomViewContainer = (FrameLayout) frameLayout.findViewById( |
17 | R.id.fullscreen_custom_content); |
18 | mErrorConsoleContainer = (LinearLayout) frameLayout |
19 | .findViewById(R.id.error_console); |
20 | setFullscreen(BrowserSettings.getInstance().useFullscreen()); |
21 | mGenericFavicon = res.getDrawable( |
22 | R.drawable.app_web_browser_sm); |
23 | mTitleBar = new TitleBar(mActivity, mUiController, this, |
24 | mContentView);//初始化titlebar |
25 | mTitleBar.setProgress(100); |
26 | mNavigationBar = mTitleBar.getNavigationBar(); |
27 | mUrlBarAutoShowManager = new UrlBarAutoShowManager(this); |
着重看这一句话:
FrameLayout frameLayout = (FrameLayout) mActivity.getWindow()
.getDecorView().findViewById(android.R.id.content);//拿到activity的content 然后把view加到一个layout上
好吧,我们知道,每个activity的DecorView都是由 title + content 组成的, 这里就是拿到了Activity的content对应的Framelayout类型的对象 ,
然后执行这个代码:
LayoutInflater.from(mActivity)
.inflate(R.layout.custom_screen, frameLayout);
这样就可以把custom_screen attach到 activityview的 conten上了, 没有用setContentView,具体原因还不是很清楚,看官可以补充.
view已经attach到activity了, 剩下的就是初始化第一个Tab以及向custom_screen中添加Tab或者其他东西了!
继续看onCreate的代码:
mController.start(icicle, getIntent());
在UI初始化ok之后就 转发给了Controller:个人认为这个函数应该叫onCreate最起码是onStart吧?总之谷歌的这个代码整体上都很随意.
1 | void start(final Bundle icicle, final Intent intent) { |
2 | boolean noCrashRecovery = intent.getBooleanExtra(NO_CRASH_RECOVERY, false);//是否设置了崩溃恢复 |
3 | if (icicle != null || noCrashRecovery) { |
4 | doStart(icicle, intent, false); |
6 | mCrashRecoveryHandler.startRecovery(intent); |
他会检查是否是崩溃重启,我们第一次启动,就是否了 执行的是doStart函数:
01 | void doStart(final Bundle icicle, final Intent intent, final boolean fromCrash) { |
03 | GoogleAccountLogin.startLoginIfNeeded(mActivity,//登陆谷歌账户 |
05 | @Override public void run() { |
07 | onPreloginFinished(icicle, intent, currentTabId, restoreIncognitoTabs, |
其他都忽略看onPreloginFinished()函数:
01 | /*!!这是浏览器 第一次启动时候的入口*/ |
02 | private void onPreloginFinished(Bundle icicle, Intent intent, long currentTabId, |
03 | boolean restoreIncognitoTabs, boolean fromCrash) { |
04 | if (currentTabId == -1) { |
05 | BackgroundHandler.execute(new PruneThumbnails(mActivity, null)); //清空缩略图缓存 |
06 | final Bundle extra = intent.getExtras(); |
07 | // Create an initial tab. |
08 | // If the intent is ACTION_VIEW and data is not null, the Browser is |
09 | // invoked to view the content by another application. In this case, |
10 | // the tab will be close when exit. |
11 | UrlData urlData = IntentHandler.getUrlDataFromIntent(intent); |
13 | if (urlData.isEmpty()) {//这里开始打开tab了 |
14 | t = openTabToHomePage();//intent没有数据 打开home |
16 | t = openTab(urlData); //打开对于url的 tab |
18 | if (t != null) {//设置调用应用的id |
19 | t.setAppId(intent.getStringExtra(Browser.EXTRA_APPLICATION_ID)); |
21 | WebView webView = t.getWebView(); |
23 | int scale = extra.getInt(Browser.INITIAL_ZOOM_LEVEL, 0); |
24 | if (scale > 0 && scale <= 1000) { |
25 | webView.setInitialScale(scale); |
28 | mUi.updateTabs(mTabControl.getTabs()); //更新多窗口列表 |
其实是执行了openToHomePage和openTab的其中一个 这两个函数一个是打开首页一个是外部app调用浏览器时候打开对应url.
我们只看openToHomePage()
2 | public Tab openTabToHomePage() { |
3 | return openTab(mSettings.getHomePage(), false, true, false); |
01 | public Tab openTab(String url, boolean incognito, boolean setActive, |
02 | boolean useCurrent, Tab parent) { |
03 | Tab tab = createNewTab(incognito, setActive, useCurrent); |
05 | if (parent != null && parent != tab) { |
06 | parent.addChildTab(tab);//一个tab中可以有子tab 就可以实现前进后退了 |
最后是调用到了createNewTab这个函数
01 | // this method will attempt to create a new tab |
02 | // incognito: private browsing tab |
03 | // setActive: ste tab as current tab |
04 | // useCurrent: if no new tab can be created, return current tab |
12 | private Tab createNewTab(boolean incognito, boolean setActive, |
15 | if (mTabControl.canCreateNewTab()) { |
16 | tab = mTabControl.createNewTab(incognito); |
23 | tab = mTabControl.getCurrentTab(); |
26 | mUi.showMaxTabsWarning(); |
那么第一个Tab就创建好了,我们就可以显示在Activity了:
在Activity的onResume函数中:是吧Activity只是一个转发各种系统回调的功能:
02 | protected void onResume() { |
05 | Log.v(LOGTAG, "BrowserActivity.onResume: this=" + this); |
07 | if (mController != null) { |
08 | mController.onResume(); |
Controller收到onResume消息, 就进行UI的一些需要resume的操作了,其实controler也是做了一次转发:
2 | mUi.onResume(); //初始化UI 设置为当前tab |
回调到了BaseUI的onResume
1 | public void onResume() { |
2 | final Tab ct = mTabControl.getCurrentTab(); //如果是从onPause后再onResume的那么就不好执行setActiveTab,因为可以拿到dang'q if (ct != null) { |
如此这般就调用到了setActiveTab这个函数 顾名思义是设置当前活动的Tab的
02 | public void setActiveTab(final Tab tab) { |
03 | mHandler.removeMessages(MSG_HIDE_TITLEBAR); |
04 | if ((tab != mActiveTab) && (mActiveTab != null)) {//以前之前的webview |
05 | removeTabFromContentView(mActiveTab); |
06 | WebView web = mActiveTab.getWebView(); |
08 | web.setOnTouchListener(null); |
12 | WebView web = mActiveTab.getWebView();//拿到新的webview窗口 |
13 | updateUrlBarAutoShowManagerTarget(); |
14 | attachTabToContentView(tab); |
15 | setShouldShowErrorConsole(tab, mUiController.shouldShowErrorConsole()); |
16 | onTabDataChanged(tab);//通知多标签数据变化了刷新多标签列表 |
17 | onProgressChanged(tab);//通知进度条数据变化了 |
这里有一个attachTabTocontentView函数,就是把当前tab 添加到上面的contentview上.
01 | protected void attachTabToContentView(Tab tab) { |
02 | if ((tab == null) || (tab.getWebView() == null)) { |
05 | View container = tab.getViewContainer(); |
06 | WebView mainView = tab.getWebView(); |
07 | // Attach the WebView to the container and then attach the |
08 | // container to the content view. |
10 | (FrameLayout) container.findViewById(R.id.webview_wrapper); |
11 | ViewGroup parent = (ViewGroup) mainView.getParent(); |
12 | if (parent != wrapper) { |
14 | Log.w(LOGTAG, "mMainView already has a parent in" |
15 | + " attachTabToContentView!"); |
16 | parent.removeView(mainView); |
18 | wrapper.addView(mainView); |
20 | Log.w(LOGTAG, "mMainView is already attached to wrapper in" |
21 | + " attachTabToContentView!"); |
23 | parent = (ViewGroup) container.getParent(); |
24 | if (parent != mContentView) { |
26 | Log.w(LOGTAG, "mContainer already has a parent in" |
27 | + " attachTabToContentView!"); |
28 | parent.removeView(container); |
30 | mContentView.addView(container, COVER_SCREEN_PARAMS);//添加tab到Contentview 这样我们就可以看到tab了 |
32 | Log.w(LOGTAG, "mContainer is already attached to content in" |
33 | + " attachTabToContentView!"); |
35 | mUiController.attachSubWindow(tab); |
通过这句话:
mContentView.addView(container, COVER_SCREEN_PARAMS);
添加tab到Contentview 这样我们就可以看到tab了!这里的Tab是一个含有View(WebView)的类, 而不是一个View,通过getWebView才真正的拿到了View并添加上, 这样Tab还可以执行以下别的操作.