BrowserActivity 是瀏覽器的核心Activity了, 是瀏覽器的入口, 可是他裏面並無出來不少複雜的邏輯, 只是實現一些android javascript
系統對activity的回調. 這些邏輯交給了Controller來處理, 就讓咱們一步一步的來看看瀏覽器是怎麼從啓動到打開Tab的 吧 java
首先是初始化Controller, 其時序圖以下: Controller 初始化了一堆瀏覽器運行相當重要的類: android
先看一下 onCreate 函數以下: web
02 |
public void onCreate(Bundle icicle) { |
04 |
Log.v(LOGTAG, this + " onStart, has state: " |
05 |
+ (icicle == null ? "false" : "true")); |
07 |
super.onCreate(icicle); |
09 |
// If this was a web search request, pass it on to the default web |
10 |
// search provider and finish this activity. |
12 |
if (IntentHandler.handleWebSearchIntent(this, null, getIntent())) { |
17 |
mController = new Controller(this, icicle == null); |
18 |
boolean xlarge = isTablet(this); |
21 |
mUi = new XLargeUi(this, mController); |
23 |
mUi = new PhoneUi(this, mController); |
26 |
mController.setUi(mUi); |
28 |
Bundle state = getIntent().getBundleExtra(EXTRA_STATE); |
29 |
if (state != null && icicle == null) { |
33 |
mController.start(icicle, getIntent()); |
是Controller這個類,這是瀏覽器的核心,我看看一次Controller都初始化什麼業務: 數據庫
01 |
public Controller(Activity browser, boolean preloadCrashState) { |
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(); //載入崩潰恢復的網頁 |
10 |
mFactory = new BrowserWebViewFactory(browser);//初始化 webview的工廠 |
12 |
mUrlHandler = new UrlHandler(this);//url處理類 |
13 |
mIntentHandler = new IntentHandler(mActivity, this);//處理各類intent在BrowserActivity也曾用到 |
14 |
mPageDialogsHandler = new PageDialogsHandler(mActivity, this); //頁面信息頁面 |
16 |
startHandler();//初始化全局的handler 這個handler 用來處理 形如 前進後退,打開書籤窗口等操做 |
17 |
mBookmarksObserver = new ContentObserver(mHandler) { //數據庫數據變化通知tabcontroller更新書籤數據 |
19 |
public void onChange(boolean selfChange) { |
20 |
int size = mTabControl.getTabCount(); |
21 |
for (int i = 0; i < size; i++) { |
22 |
mTabControl.getTab(i).updateBookmarkedStatus(); |
27 |
browser.getContentResolver().registerContentObserver( |
28 |
BrowserContract.Bookmarks.CONTENT_URI, true, mBookmarksObserver);//註冊這個觀察者 |
30 |
mNetworkHandler = new NetworkStateHandler(mActivity, this); //網絡變化監聽 |
31 |
// Start watching the default geolocation permissions |
32 |
mSystemAllowGeolocationOrigins = |
33 |
new SystemAllowGeolocationOrigins(mActivity.getApplicationContext()); //地理位置信息服務 |
34 |
mSystemAllowGeolocationOrigins.start(); |
36 |
openIconDatabase();//網址圖標 |
初始化ok了Controller以後就是設置View了, 這個在上一篇文章已經提到, 再也不贅述. 咱們看Activity的onResume函數中: 瀏覽器
02 |
protected void onResume() { |
05 |
Log.v(LOGTAG, "BrowserActivity.onResume: this=" + this); |
07 |
if (mController != null) { |
08 |
mController.onResume(); |
回調到了Controller的onResume 咱們也說過 Activity的功能基本上都是轉發
02 |
if (!mActivityPaused) { //android 常常有這樣的標誌位判斷是否 是發生了Pause由於 從pause 和start都會執行onResume |
03 |
Log.e(LOGTAG, "BrowserActivity is already resumed."); |
06 |
mActivityPaused = false; |
07 |
Tab current = mTabControl.getCurrentTab(); |
08 |
if (current != null) { |
09 |
current.resume();//恢復當前的tab |
10 |
resumeWebViewTimers(current); //是否恢復webview的解析js執行等功能 |
12 |
releaseWakeLock(); //更換cpu模式 |
14 |
mUi.onResume(); //初始化UI 設置爲當前tab |
15 |
mNetworkHandler.onResume(); //註冊網絡變化通知 |
16 |
WebView.enablePlatformNotifications(); |
17 |
NfcHandler.register(mActivity, this); //註冊nfc |
mUI.onResume() 網絡
回調到了BaseUI的onResume app
1 |
public void onResume() { |
2 |
mActivityPaused = false; |
3 |
// check if we exited without setting active tab |
5 |
final Tab ct = mTabControl.getCurrentTab(); |
7 |
setActiveTab(ct);//設置當前的Tab |
這裏就是設置當前的Tab了. less
瀏覽器瀏覽器的啓動其實有兩種方式: 1.經過Launcher 啓動 2.其餘app調用瀏覽器啓動 , 對於第二種啓動, 若是瀏覽器在後臺, 就直接執行onNewIntent函數若是不在後臺, 會先onCreate 而後再 onNewIntent: dom
03 |
* browseractivity的 launchMode爲singleTask的時候,經過Intent啓到一個Activity,若是系統已經存在一個實例, |
04 |
* 系統就會將請求發送到這個實例上,但這個時候,系統就不會再調用一般狀況下咱們處理請求數據的onCreate方法, |
05 |
* 而是調用onNewIntent方法,以下所示: |
08 |
protected void onNewIntent(Intent intent) { |
09 |
if (ACTION_RESTART.equals(intent.getAction())) { |
10 |
Bundle outState = new Bundle(); |
11 |
mController.onSaveInstanceState(outState); |
13 |
//是否完全重啓瀏覽器? 這樣 會調用onCreate 而不是這裏 |
14 |
getApplicationContext().startActivity( |
15 |
new Intent(getApplicationContext(), BrowserActivity.class) |
16 |
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) |
17 |
.putExtra(EXTRA_STATE, outState)); |
20 |
mController.handleNewIntent(intent); |
其實仍是轉發Controller:
2 |
public void handleNewIntent(Intent intent) { |
3 |
if (!mUi.isWebShowing()) { |
6 |
mIntentHandler.onNewIntent(intent); |
其中主要的任務是三個:
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. |
012 |
mController.setActiveTab(current);//在當前頁面打開傳入的url |
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 |
022 |
if (BrowserActivity.ACTION_SHOW_BOOKMARKS.equals(action)) { |
024 |
mController.bookmarksOrHistoryPicker(ComboViews.Bookmarks); |
028 |
// In case the SearchDialog is open. |
029 |
((SearchManager) mActivity.getSystemService(Context.SEARCH_SERVICE)) |
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. |
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); |
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. |
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)) { |
072 |
UrlData urlData = getUrlDataFromIntent(intent); |
073 |
if (urlData.isEmpty()) { |
074 |
urlData = new UrlData(mSettings.getHomePage()); |
077 |
if (intent.getBooleanExtra(Browser.EXTRA_CREATE_NEW_TAB, false) |
078 |
|| urlData.isPreloaded()) { |
079 |
Tab t = mController.openTab(urlData);//窗口新的tab |
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 |
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); |
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); |
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); |
120 |
// Otherwise, we are already viewing the correct tab. |
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 |
127 |
Tab tab = mController.openTab(urlData); |
130 |
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) { |
131 |
tab.setCloseOnBack(true); |
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(); |
152 |
mSettings.toggleDebugSettings(); |
156 |
// Get rid of the subwindow if it exists |
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); |
164 |
mController.loadUrlDataIn(current, urlData); |
而後執行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的學習
可能還有須要研究的點,待後續補充!