从之前的文章中我们可以看到, BrowserActivity 是浏览器的核心Activity了, 是浏览器的入口, 但是他里面并没有处理很多复杂的逻辑, 只是实现一些android
先看看时序图
系统对activity的回调. 这些逻辑交给了Controller来处理, 那我们首先先入门一下, 一步一步的来看看浏览器是怎么从启动到打开Tab的 吧
从入口 BrowserActivity的 onCreate 函数开始:
01@Override02 public void onCreate(Bundle icicle) {03 04 //初始化核心的Controller05 mController = new Controller(this, icicle == null);06 boolean xlarge = isTablet(this);07 //处理pad和phone08 if (xlarge) {09 mUi = new XLargeUi(this, mController);10 } else {11 mUi = new PhoneUi(this, mController);12 }13 //设置UI14 mController.setUi(mUi);15 16 }
C mController = new Controller(this, icicle == null);
Controller这个类,这是浏览器的核心, 其他我们暂时忽略这里只看TabControl的初始化:
1public Controller(Activity browser, boolean preloadCrashState) {2 3 mTabControl = new TabControl(this); //初始化tab的控制器4 5}
mTabControl = new TabControl(this); //初始化tab的控制器, TabControl是管理所有Tab的Controller , 将来添加Tab添加到这个 链表中.
然后 , 如果是手机就会执行 pad版本大同小异就不做介绍了!
mUi = new PhoneUi(this, mController); 这句话, 代码如下
01/**02 * @param browser03 * @param controller04 */05 public PhoneUi(Activity browser, UiController controller) {06 super(browser, controller);07 setUseQuickControls(BrowserSettings.getInstance().useQuickControls()); //设置快速控制菜单,就是那个piemenu08 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());14 } 首先会调用BaseUI的构造函数 这里会执行一系列的View的初始化,这里传入了activity对象,所以可以设置activity的各种UI
01public BaseUi(Activity browser, UiController controller) {02 mActivity = browser;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(15 R.id.main_content);16 mCustomViewContainer = (FrameLayout) frameLayout.findViewById(17 R.id.fullscreen_custom_content);18 mErrorConsoleContainer = (LinearLayout) frameLayout19 .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);//初始化titlebar25 mTitleBar.setProgress(100);26 mNavigationBar = mTitleBar.getNavigationBar();27 mUrlBarAutoShowManager = new UrlBarAutoShowManager(this);28 } 着重看这一句话:
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吧?总之谷歌的这个代码整体上都很随意.
1void 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);5 } else {6 mCrashRecoveryHandler.startRecovery(intent);7 }8 }
他会检查是否是崩溃重启,我们第一次启动,就是否了 执行的是doStart函数:
01void doStart(final Bundle icicle, final Intent intent, final boolean fromCrash) {02 03 GoogleAccountLogin.startLoginIfNeeded(mActivity,//登陆谷歌账户04 new Runnable() {05 @Override public void run() {06 //登陆之后在处理一下其他操作07 onPreloginFinished(icicle, intent, currentTabId, restoreIncognitoTabs,08 fromCrash);09 }10 });11 }
其他都忽略看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 is09 // 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);12 Tab t = null;13 if (urlData.isEmpty()) {//这里开始打开tab了14 t = openTabToHomePage();//intent没有数据 打开home15 } else {16 t = openTab(urlData); //打开对于url的 tab17 }18 if (t != null) {//设置调用应用的id19 t.setAppId(intent.getStringExtra(Browser.EXTRA_APPLICATION_ID));20 }21 WebView webView = t.getWebView();22 if (extra != null) {23 int scale = extra.getInt(Browser.INITIAL_ZOOM_LEVEL, 0);24 if (scale > 0 && scale <= 1000) {25 webView.setInitialScale(scale);26 }27 }28 mUi.updateTabs(mTabControl.getTabs()); //更新多窗口列表29 }30 }
其实是执行了openToHomePage和openTab的其中一个 这两个函数一个是打开首页一个是外部app调用浏览器时候打开对应url.
我们只看openToHomePage()
1@Override2 public Tab openTabToHomePage() {3 return openTab(mSettings.getHomePage(), false, true, false);4 }
01public Tab openTab(String url, boolean incognito, boolean setActive,02 boolean useCurrent, Tab parent) {03 Tab tab = createNewTab(incognito, setActive, useCurrent);04 if (tab != null) {05 if (parent != null && parent != tab) {06 parent.addChildTab(tab);//一个tab中可以有子tab 就可以实现前进后退了07 }08 if (url != null) {09 loadUrl(tab, url);10 }11 }12 return tab;13 }
最后是调用到了createNewTab这个函数
01// this method will attempt to create a new tab02 // incognito: private browsing tab03 // setActive: ste tab as current tab04 // useCurrent: if no new tab can be created, return current tab05 /**06 * 创建一个新的tab 选择是否重用当前的07 * @param incognito08 * @param setActive09 * @param useCurrent10 * @return11 */12 private Tab createNewTab(boolean incognito, boolean setActive,13 boolean useCurrent) {14 Tab tab = null;15 if (mTabControl.canCreateNewTab()) {16 tab = mTabControl.createNewTab(incognito);17 addTab(tab);18 if (setActive) {19 setActiveTab(tab);20 }21 } else {22 if (useCurrent) {23 tab = mTabControl.getCurrentTab();24 reuseTab(tab, null);25 } else {26 mUi.showMaxTabsWarning();27 }28 }29 return tab;30 }
那么第一个Tab就创建好了,我们就可以显示在Activity了:
在Activity的onResume函数中:是吧Activity只是一个转发各种系统回调的功能:
01@Override02 protected void onResume() {03 super.onResume();04 if (LOGV_ENABLED) {05 Log.v(LOGTAG, "BrowserActivity.onResume: this=" + this);06 }07 if (mController != null) {08 mController.onResume();09 }10 }
Controller收到onResume消息, 就进行UI的一些需要resume的操作了,其实controler也是做了一次转发:
1void onResume() {2 mUi.onResume(); //初始化UI 设置为当前tab3 } 回调到了BaseUI的onResume
1public void onResume() {2 final Tab ct = mTabControl.getCurrentTab(); //如果是从onPause后再onResume的那么就不好执行setActiveTab,因为可以拿到dang'q if (ct != null) {3 setActiveTab(ct);4 }5 } 如此这般就调用到了setActiveTab这个函数 顾名思义是设置当前活动的Tab的
01@Override02 public void setActiveTab(final Tab tab) {03 mHandler.removeMessages(MSG_HIDE_TITLEBAR);04 if ((tab != mActiveTab) && (mActiveTab != null)) {//以前之前的webview05 removeTabFromContentView(mActiveTab);06 WebView web = mActiveTab.getWebView();07 if (web != null) {08 web.setOnTouchListener(null);09 }10 }11 mActiveTab = tab;12 WebView web = mActiveTab.getWebView();//拿到新的webview窗口13 updateUrlBarAutoShowManagerTarget();14 attachTabToContentView(tab);15 setShouldShowErrorConsole(tab, mUiController.shouldShowErrorConsole());16 onTabDataChanged(tab);//通知多标签数据变化了刷新多标签列表17 onProgressChanged(tab);//通知进度条数据变化了18 19 } 这里有一个attachTabTocontentView函数,就是把当前tab 添加到上面的contentview上.
01protected void attachTabToContentView(Tab tab) {02 if ((tab == null) || (tab.getWebView() == null)) {03 return;04 }05 View container = tab.getViewContainer();06 WebView mainView = tab.getWebView();07 // Attach the WebView to the container and then attach the08 // container to the content view.09 FrameLayout wrapper =10 (FrameLayout) container.findViewById(R.id.webview_wrapper);11 ViewGroup parent = (ViewGroup) mainView.getParent();12 if (parent != wrapper) {13 if (parent != null) {14 Log.w(LOGTAG, "mMainView already has a parent in"15 + " attachTabToContentView!");16 parent.removeView(mainView);17 }18 wrapper.addView(mainView);19 } else {20 Log.w(LOGTAG, "mMainView is already attached to wrapper in"21 + " attachTabToContentView!");22 }23 parent = (ViewGroup) container.getParent();24 if (parent != mContentView) {25 if (parent != null) {26 Log.w(LOGTAG, "mContainer already has a parent in"27 + " attachTabToContentView!");28 parent.removeView(container);29 }30 mContentView.addView(container, COVER_SCREEN_PARAMS);//添加tab到Contentview 这样我们就可以看到tab了31 } else {32 Log.w(LOGTAG, "mContainer is already attached to content in"33 + " attachTabToContentView!");34 }35 mUiController.attachSubWindow(tab);36 } 通过这句话:
mContentView.addView(container, COVER_SCREEN_PARAMS);
添加tab到Contentview 这样我们就可以看到tab了!这里的Tab是一个含有View(WebView)的类, 而不是一个View,通过getWebView才真正的拿到了View并添加上, 这样Tab还可以执行以下别的操作.
原文地址:http://my.oschina.net/sfshine/blog/197803
0 0