Android Browser学习二 BrowserActivity 的初始化 --其他重要模块

BrowserActivity 是浏览器的核心Activity了, 是浏览器的入口, 但是他里面并没有出来很多复杂的逻辑, 只是实现一些android

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

首先是初始化Controller, 其时序图如下: Controller 初始化了一堆浏览器运行至关重要的类:

先看一下 onCreate 函数如下:

01 @Override
02   public void onCreate(Bundle icicle) {
03       if (LOGV_ENABLED) {
04           Log.v(LOGTAG, this + " onStart, has state: "
05                   + (icicle == null ? "false" : "true"));
06       }
07       super.onCreate(icicle);
08  
09       // If this was a web search request, pass it on to the default web
10       // search provider and finish this activity.
11       //处理来自搜索的intent
12       if (IntentHandler.handleWebSearchIntent(this, null, getIntent())) {
13           finish();
14           return;
15       }
16       //初始化核心的Controller
17       mController = new Controller(this, icicle == null);
18       boolean xlarge = isTablet(this);
19       //处理pad和phone
20       if (xlarge) {
21           mUi = new XLargeUi(this, mController);
22       } else {
23           mUi = new PhoneUi(this, mController);
24       }
25       //设置UI
26       mController.setUi(mUi);
27       //是否恢复上一层的一些状态
28       Bundle state = getIntent().getBundleExtra(EXTRA_STATE);
29       if (state != null && icicle == null) {
30           icicle = state;
31       }
32  
33       mController.start(icicle, getIntent());
34   }

是Controller这个类,这是浏览器的核心,我看看一次Controller都初始化什么业务:

01 public Controller(Activity browser, boolean preloadCrashState) {
02     mActivity = browser;
03     mSettings = BrowserSettings.getInstance(); //拿到BrowserSetting 的设置实例
04     mTabControl = new TabControl(this); //初始化tab的控制器
05     mSettings.setController(this);//Setting的实例也需要从controller 中设置一些 东西
06     mCrashRecoveryHandler = CrashRecoveryHandler.initialize(this); //崩溃处理注册
07     if (preloadCrashState) {
08         mCrashRecoveryHandler.preloadCrashState(); //载入崩溃恢复的网页
09     }
10     mFactory = new BrowserWebViewFactory(browser);//初始化 webview的工厂
11  
12     mUrlHandler = new UrlHandler(this);//url处理类
13     mIntentHandler = new IntentHandler(mActivity, this);//处理各种intent在BrowserActivity也曾用到
14     mPageDialogsHandler = new PageDialogsHandler(mActivity, this); //页面信息页面
15  
16     startHandler();//初始化全局的handler 这个handler 用来处理 形如 前进后退,打开书签窗口等操作
17     mBookmarksObserver = new ContentObserver(mHandler) { //数据库数据变化通知tabcontroller更新书签数据
18         @Override
19         public void onChange(boolean selfChange) {
20             int size = mTabControl.getTabCount();
21             for (int i = 0; i < size; i++) {
22                 mTabControl.getTab(i).updateBookmarkedStatus();
23             }
24         }
25  
26     };
27     browser.getContentResolver().registerContentObserver(
28             BrowserContract.Bookmarks.CONTENT_URI, true, mBookmarksObserver);//注册这个观察者
29  
30     mNetworkHandler = new NetworkStateHandler(mActivity, this); //网络变化监听
31     // Start watching the default geolocation permissions
32     mSystemAllowGeolocationOrigins =
33             new SystemAllowGeolocationOrigins(mActivity.getApplicationContext()); //地理位置信息服务
34     mSystemAllowGeolocationOrigins.start();
35  
36     openIconDatabase();//网址图标
37 }

初始化ok了Controller之后就是设置View了, 这个在上一篇文章已经提到, 不再赘述.  我们看Activity的onResume函数中:

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 我们也说过 Activity的功能基本上都是转发

01 void onResume() {
02         if (!mActivityPaused) { //android 经常有这样的标志位判断是否 是发生了Pause因为 从pause 和start都会执行onResume
03             Log.e(LOGTAG, "BrowserActivity is already resumed.");
04             return;
05         }
06         mActivityPaused = false;
07         Tab current = mTabControl.getCurrentTab();
08         if (current != null) {
09             current.resume();//恢复当前的tab
10             resumeWebViewTimers(current); //是否恢复webview的解析js执行等功能
11         }
12         releaseWakeLock(); //更换cpu模式
13  
14         mUi.onResume(); //初始化UI 设置为当前tab
15         mNetworkHandler.onResume(); //注册网络变化通知
16         WebView.enablePlatformNotifications();
17         NfcHandler.register(mActivity, this); //注册nfc
18     }

mUI.onResume()

回调到了BaseUI的onResume

1 public void onResume() {
2        mActivityPaused = false;
3        // check if we exited without setting active tab
4        // b: 5188145
5        final Tab ct = mTabControl.getCurrentTab();
6        if (ct != null) {
7            setActiveTab(ct);//设置当前的Tab
8        }
9    }

这里就是设置当前的Tab了.

浏览器浏览器的启动其实有两种方式: 1.通过Launcher 启动 2.其他app调用浏览器启动 , 对于第二种启动, 如果浏览器在后台, 就直接执行onNewIntent函数如果不在后台, 会先onCreate 然后再 onNewIntent:

01 /*
02     * 处理从外部调用浏览器的intent
03     * browseractivity的 launchMode为singleTask的时候,通过Intent启到一个Activity,如果系统已经存在一个实例,
04     * 系统就会将请求发送到这个实例上,但这个时候,系统就不会再调用通常情况下我们处理请求数据的onCreate方法,
05     * 而是调用onNewIntent方法,如下所示:
06     */
07    @Override
08    protected void onNewIntent(Intent intent) {
09        if (ACTION_RESTART.equals(intent.getAction())) {
10            Bundle outState = new Bundle();
11            mController.onSaveInstanceState(outState);
12            finish();
13            //是否彻底重启浏览器? 这样 会调用onCreate 而不是这里
14            getApplicationContext().startActivity(
15                    new Intent(getApplicationContext(), BrowserActivity.class)
16                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
17                    .putExtra(EXTRA_STATE, outState));
18            return;
19        }
20        mController.handleNewIntent(intent);
21    }

其实还是转发Controller:

1 @Override
2  public void handleNewIntent(Intent intent) {
3      if (!mUi.isWebShowing()) {
4          mUi.showWeb(false);
5      }
6      mIntentHandler.onNewIntent(intent);
7  }

其中主要的任务是三个:

1.打开书签 历史窗口选择书签等

2.打开传入的Url

3.可能传入的是关键词, 用户调用浏览器进行搜索

其中还包括了形如 复用Tab等代码处理逻辑

001 void onNewIntent(Intent intent) {
002         Tab current = mTabControl.getCurrentTab();
003         // When a tab is closed on exit, the current tab index is set to -1.
004         // Reset before proceed as Browser requires the current tab to be set.
005         if (current == null) {
006             // Try to reset the tab in case the index was incorrect.
007             current = mTabControl.getTab(0);
008             if (current == null) {
009                 // No tabs at all so just ignore this intent.
010                 return;
011             }
012             mController.setActiveTab(current);//在当前页面打开传入的url
013         }
014         final String action = intent.getAction();
015         final int flags = intent.getFlags();
016         if (Intent.ACTION_MAIN.equals(action) ||
017                 (flags & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
018             // just resume the browser
019             //正常启动浏览器或者从书签窗口打开
020             return;
021         }
022         if (BrowserActivity.ACTION_SHOW_BOOKMARKS.equals(action)) {
023             //可以直接从外面跳转到书签历史选择
024             mController.bookmarksOrHistoryPicker(ComboViews.Bookmarks);
025             return;
026         }
027  
028         // In case the SearchDialog is open.
029         ((SearchManager) mActivity.getSystemService(Context.SEARCH_SERVICE))
030                 .stopSearch();
031         boolean activateVoiceSearch = RecognizerResultsIntent
032                 .ACTION_VOICE_SEARCH_RESULTS.equals(action);
033         if (Intent.ACTION_VIEW.equals(action)
034                 || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)
035                 || Intent.ACTION_SEARCH.equals(action)
036                 || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
037                 || Intent.ACTION_WEB_SEARCH.equals(action)
038                 || activateVoiceSearch) {
039             if (current.isInVoiceSearchMode()) {//是否语音输入
040                 String title = current.getVoiceDisplayTitle();
041                 if (title != null && title.equals(intent.getStringExtra(
042                         SearchManager.QUERY))) {
043                     // The user submitted the same search as the last voice
044                     // search, so do nothing.
045                     //如果搜索和上次一样 什么也不做
046                     return;
047                 }
048                 //处理来自搜索的intent
049                 if (Intent.ACTION_SEARCH.equals(action)
050                         && current.voiceSearchSourceIsGoogle()) {
051                     Intent logIntent = new Intent(
052                             LoggingEvents.ACTION_LOG_EVENT);
053                     logIntent.putExtra(LoggingEvents.EXTRA_EVENT,
054                             LoggingEvents.VoiceSearch.QUERY_UPDATED);
055                     logIntent.putExtra(
056                             LoggingEvents.VoiceSearch.EXTRA_QUERY_UPDATED_VALUE,
057                             intent.getDataString());
058                     mActivity.sendBroadcast(logIntent);
059                     // Note, onPageStarted will revert the voice title bar
060                     // When http://b/issue?id=2379215 is fixed, we should update
061                     // the title bar here.
062                 }
063             }
064             // If this was a search request (e.g. search query directly typed into the address bar),
065             // pass it on to the default web search provider.
066             //如果是搜索词 就直接开始搜索, 其实请求的是 打开Intent.ACTION_WEB_SEARCH这个action
067             if (handleWebSearchIntent(mActivity, mController, intent)) {
068                 return;
069             }
070              
071             //开始打开url
072             UrlData urlData = getUrlDataFromIntent(intent);
073             if (urlData.isEmpty()) {
074                 urlData = new UrlData(mSettings.getHomePage());
075             }
076  
077             if (intent.getBooleanExtra(Browser.EXTRA_CREATE_NEW_TAB, false)
078                   || urlData.isPreloaded()) {
079                 Tab t = mController.openTab(urlData);//窗口新的tab
080                 return;
081             }
082             /*
083              * TODO: Don't allow javascript URIs
084              * 0) If this is a javascript: URI, *always* open a new tab
085              * 1) If this is a voice search, re-use tab for appId
086              *    If there is no appId, use current tab
087              * 2) If the URL is already opened, switch to that tab //如果url已经打开了 使用当前tab
088              * 3-phone) Reuse tab with same appId //对于同一个app 重用其tab
089              * 3-tablet) Open new tab
090              */
091             final String appId = intent
092                     .getStringExtra(Browser.EXTRA_APPLICATION_ID);
093             if (!TextUtils.isEmpty(urlData.mUrl) &&
094                     urlData.mUrl.startsWith("javascript:")) {
095                 // Always open javascript: URIs in new tabs
096                 mController.openTab(urlData);
097                 return;
098             }
099             if ((Intent.ACTION_VIEW.equals(action)
100                     // If a voice search has no appId, it means that it came
101                     // from the browser.  In that case, reuse the current tab.
102                     || (activateVoiceSearch && appId != null))
103                     && !mActivity.getPackageName().equals(appId)) {
104                 if (activateVoiceSearch || !BrowserActivity.isTablet(mActivity)) {
105                     Tab appTab = mTabControl.getTabFromAppId(appId);
106                     if (appTab != null) {
107                         mController.reuseTab(appTab, urlData);
108                         return;
109                     }
110                 }
111                 // No matching application tab, try to find a regular tab
112                 // with a matching url.
113                 Tab appTab = mTabControl.findTabWithUrl(urlData.mUrl);
114                 if (appTab != null) {
115                     // Transfer ownership
116                     appTab.setAppId(appId);
117                     if (current != appTab) {
118                         mController.switchToTab(appTab);
119                     }
120                     // Otherwise, we are already viewing the correct tab.
121                 } else {
122                     // if FLAG_ACTIVITY_BROUGHT_TO_FRONT flag is on, the url
123                     // will be opened in a new tab unless we have reached
124                     // MAX_TABS. Then the url will be opened in the current
125                     // tab. If a new tab is created, it will have "true" for
126                     // exit on close.
127                     Tab tab = mController.openTab(urlData);
128                     if (tab != null) {
129                         tab.setAppId(appId);
130                         if ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
131                             tab.setCloseOnBack(true);
132                         }
133                     }
134                 }
135             } else {
136                 //一堆config的处理
137                 if (!urlData.isEmpty()
138                         && urlData.mUrl.startsWith("about:debug")) {
139                     if ("about:debug.dom".equals(urlData.mUrl)) {
140                         current.getWebView().dumpDomTree(false);
141                     } else if ("about:debug.dom.file".equals(urlData.mUrl)) {
142                         current.getWebView().dumpDomTree(true);
143                     } else if ("about:debug.render".equals(urlData.mUrl)) {
144                         current.getWebView().dumpRenderTree(false);
145                     } else if ("about:debug.render.file".equals(urlData.mUrl)) {
146                         current.getWebView().dumpRenderTree(true);
147                     } else if ("about:debug.display".equals(urlData.mUrl)) {
148                         current.getWebView().dumpDisplayTree();
149                     } else if ("about:debug.nav".equals(urlData.mUrl)) {
150                         current.getWebView().debugDump();
151                     } else {
152                         mSettings.toggleDebugSettings();
153                     }
154                     return;
155                 }
156                 // Get rid of the subwindow if it exists
157                 //去除二级窗口
158                 mController.dismissSubWindow(current);
159                 // If the current Tab is being used as an application tab,
160                 // remove the association, since the new Intent means that it is
161                 // no longer associated with that application.
162                 current.setAppId(null);
163                 //开始载入url
164                 mController.loadUrlDataIn(current, urlData);
165             }
166         }
167     }

然后执行OnResume就可以展现用户需要的窗口了!

Browser还有一个重要的结构是BaseUI, Browser的UI操作基本都限制在了BaseUI中, 当需要展示某个UI了, BaseUI会通知Controller, 然后Controller 开始调用显示各种用户交换元素:

针对手机和平板的UI不同但是功能差不多, 所有又有PhoneUI和XLargeUi继承BaseUI实现其功能, 这和Android系统的设计大同小异

的确Controller是Browser的核心, 计划从几个方面来分析:

1.TabControl的逻辑也就是多窗口切换的逻辑

2.BookMarkControl的处理逻辑也就是书签历史和保存网页

3.PieControl的学习 也就是快捷控制菜单的学习

4.框计算的学习, 就是TitleBar

5.Url 的处理 包括判断 猜测url fix Url等

6.CrashRecoveryHandler ,的学习

7.NetworkStateHandler的学习

可能还有需要研究的点,待后续补充!

原文地址:https://www.cnblogs.com/tonglingqijie/p/4710751.html