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

从之前的文章中我们可以看到, 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的初始化:


1 public 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 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

01 public 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吧?总之谷歌的这个代码整体上都很随意.


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);
5        } else {
6            mCrashRecoveryHandler.startRecovery(intent);
7        }
8    }



他会检查是否是崩溃重启,我们第一次启动,就是否了 执行的是doStart函数:



01 void 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    }



01 public 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也是做了一次转发:

1 void onResume() {
2        mUi.onResume(); //初始化UI 设置为当前tab
3     }

回调到了BaseUI的onResume

1 public 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上.


01 protected 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值