Android Browser学习三 多窗口: 展示第一个Tab的过程

来源:互联网 发布:mac装win10鼠标不能用 编辑:程序博客网 时间:2024/06/04 18:14

从之前的文章中我们可以看到, BrowserActivity 是浏览器的核心Activity了, 是浏览器的入口, 但是他里面并没有处理很多复杂的逻辑, 只是实现一些android

先看看时序图



系统对activity的回调. 这些逻辑交给了Controller来处理, 那我们首先先入门一下, 一步一步的来看看浏览器是怎么从启动到打开Tab的 吧

从入口 BrowserActivity的  onCreate 函数开始:


01@Override
02  public void onCreate(Bundle icicle) {
03 
04      //初始化核心的Controller
05      mController = new Controller(this, icicle == null);
06      boolean xlarge = isTablet(this);
07      //处理pad和phone
08      if (xlarge) {
09          mUi = new XLargeUi(this, mController);
10      } else {
11          mUi = new PhoneUi(this, mController);
12      }
13      //设置UI
14      mController.setUi(mUi);
15 
16  }


C mController = new Controller(this, icicle == null);

Controller这个类,这是浏览器的核心, 其他我们暂时忽略这里只看TabControl的初始化:


1public Controller(Activity browser, boolean preloadCrashState) {
3    mTabControl = new TabControl(this); //初始化tab的控制器
5}


 mTabControl = new TabControl(this); //初始化tab的控制器, TabControl是管理所有Tab的Controller , 将来添加Tab添加到这个 链表中.

 然后 , 如果是手机就会执行 pad版本大同小异就不做介绍了!

  mUi = new PhoneUi(this, mController); 这句话, 代码如下


01/**
02   * @param browser
03   * @param controller
04   */
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());
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) 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);
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 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);
12           Tab t = null;
13           if (urlData.isEmpty()) {//这里开始打开tab了
14               t = openTabToHomePage();//intent没有数据 打开home
15           } else {
16               t = openTab(urlData); //打开对于url的 tab
17           }
18           if (t != null) {//设置调用应用的id
19               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@Override
2   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 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
05    /**
06     * 创建一个新的tab 选择是否重用当前的
07     * @param incognito
08     * @param setActive
09     * @param useCurrent
10     * @return
11     */
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@Override
02  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 设置为当前tab
3    }

回调到了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@Override
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();
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 the
08       // 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
原创粉丝点击