Android Browser學習二 BrowserActivity 的初始化 --其餘重要模塊

BrowserActivity 是瀏覽器的核心Activity了, 是瀏覽器的入口, 可是他裏面並無出來不少複雜的邏輯, 只是實現一些android javascript

系統對activity的回調. 這些邏輯交給了Controller來處理, 就讓咱們一步一步的來看看瀏覽器是怎麼從啓動到打開Tab的 吧 java

首先是初始化Controller, 其時序圖以下: Controller 初始化了一堆瀏覽器運行相當重要的類: android

先看一下 onCreate 函數以下: web


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 app


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了. less


瀏覽器瀏覽器的啓動其實有兩種方式: 1.經過Launcher 啓動 2.其餘app調用瀏覽器啓動 , 對於第二種啓動, 若是瀏覽器在後臺, 就直接執行onNewIntent函數若是不在後臺, 會先onCreate 而後再 onNewIntent: dom


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的學習


可能還有須要研究的點,待後續補充!

相關文章
相關標籤/搜索