整理了一下Browser系列, 之前写的太乱了. java
Tab是浏览器和用户打交道的主要UI,浏览器的最主要的功能--上网就是有他来完成了.上一篇文章咱们已经看到了 web
BrowserActivity是如何展示的第一个Tab, 这里咱们看一下Tab的初始化: 浏览器
TabControl是管理整个窗口切换的逻辑, 咱们打开浏览器标签窗口, 切换窗口, 关闭窗口, 都是经过TabControl实现的.可见TabControl是一个很是重要的部件 缓存
在Controller中, 首先初始化的也是TabControl, 其构造函数以下: cookie
1 /**
2 * Construct a new TabControl object
3 */
4 TabControl(Controller controller) {
5 mController = controller;//拿到Controller的引用, 未来会作一些 浏览器相关的事情
6 mMaxTabs = mController.getMaxTabs(); //最多支持多少tab
7 mTabs = new ArrayList<Tab>(mMaxTabs);//这个队列用来维护打开的tab tabs的删除添加等都须要添加到他上面,可是他不能知道哪一个tab是最后打开的
8 mTabQueue = new ArrayList<Tab>(mMaxTabs);//这个队列用来按必定的顺序存放tab 最近访问的view在最后面
9 }
|
|
初始化ok 了TabControl以后, 从Acitivity的 onCreate中就会开始启动业务逻辑 网络
1 |
mController.start(icicle, getIntent()); |
这个作一下操做以后会开始启动Tabcontrol session
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); |
6 |
mCrashRecoveryHandler.startRecovery(intent); |
02 |
void doStart(final Bundle icicle, final Intent intent, final boolean fromCrash) { |
03 |
// Unless the last browser usage was within 24 hours, destroy any |
04 |
// remaining incognito tabs. |
06 |
Calendar lastActiveDate = icicle != null ? |
07 |
(Calendar) icicle.getSerializable("lastActiveDate") : null; |
08 |
Calendar today = Calendar.getInstance(); |
09 |
Calendar yesterday = Calendar.getInstance(); |
10 |
yesterday.add(Calendar.DATE, -1); |
12 |
final boolean restoreIncognitoTabs = !(lastActiveDate == null |
13 |
|| lastActiveDate.before(yesterday) //当天启动的才会恢复之前的页面 |
14 |
|| lastActiveDate.after(today)); |
16 |
// Find out if we will restore any state and remember the tab. |
17 |
//是否能够恢复这个tab 若是能够恢复就返回其id不然 -1 |
18 |
final long currentTabId = |
19 |
mTabControl.canRestoreState(icicle, restoreIncognitoTabs); |
21 |
if (currentTabId == -1) { |
22 |
// Not able to restore so we go ahead and clear session cookies. We |
23 |
// must do this before trying to login the user as we don't want to |
24 |
// clear any session cookies set during login. 若是没有恢复的tab就清楚session |
25 |
CookieManager.getInstance().removeSessionCookie(); |
28 |
GoogleAccountLogin.startLoginIfNeeded(mActivity,//登录谷歌帐户 |
30 |
@Override public void run() { |
32 |
onPreloginFinished(icicle, intent, currentTabId, restoreIncognitoTabs, |
在这里有个获取 须要恢复窗口id的操做 final long currentTabId = mTabControl.canRestoreState(icicle, restoreIncognitoTabs); app
01 |
/** final long currentTabId = |
02 |
mTabControl.canRestoreState(icicle, restoreIncognitoTabs); <span></span> * Check if the state can be restored. If the state can be restored, the |
03 |
* current tab id is returned. This can be passed to restoreState below |
04 |
* in order to restore the correct tab. Otherwise, -1 is returned and the |
05 |
* state cannot be restored. |
07 |
long canRestoreState(Bundle inState, boolean restoreIncognitoTabs) { |
08 |
final long[] ids = (inState == null) ? null : inState.getLongArray(POSITIONS); |
12 |
final long oldcurrent = inState.getLong(CURRENT); |
14 |
if (restoreIncognitoTabs || (hasState(oldcurrent, inState) && !isIncognito(oldcurrent, inState))) { |
17 |
// pick first non incognito tab |
19 |
if (hasState(id, inState) && !isIncognito(id, inState)) { |
28 |
private boolean hasState(long id, Bundle state) { |
29 |
if (id == -1) return false; |
30 |
Bundle tab = state.getBundle(Long.toString(id)); |
31 |
return ((tab != null) && !tab.isEmpty()); |
33 |
//是不是隐身窗口, 隐身窗口是不该该恢复的 |
34 |
private boolean isIncognito(long id, Bundle state) { |
35 |
Bundle tabstate = state.getBundle(Long.toString(id)); |
36 |
if ((tabstate != null) && !tabstate.isEmpty()) { |
37 |
return tabstate.getBoolean(Tab.INCOGNITO); |
后面的代码其实仍是在onPreloginFinished函数中: less
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); |
13 |
if (urlData.isEmpty()) {//这里开始打开tab了 |
14 |
t = openTabToHomePage();//intent没有数据 打开home |
16 |
t = openTab(urlData); //打开对于url的 tab |
18 |
if (t != null) {//设置调用应用的id |
19 |
t.setAppId(intent.getStringExtra(Browser.EXTRA_APPLICATION_ID)); |
21 |
WebView webView = t.getWebView(); |
23 |
int scale = extra.getInt(Browser.INITIAL_ZOOM_LEVEL, 0); |
24 |
if (scale > 0 && scale <= 1000) { |
25 |
webView.setInitialScale(scale); |
28 |
mUi.updateTabs(mTabControl.getTabs()); //更新多窗口列表 |
29 |
} else {//这部分代码处理的是获取一下意外退出销毁的Tab的过程: |
30 |
mTabControl.restoreState(icicle, currentTabId, restoreIncognitoTabs, |
31 |
mUi.needsRestoreAllTabs()); |
32 |
List<Tab> tabs = mTabControl.getTabs(); |
33 |
ArrayList<Long> restoredTabs = new ArrayList<Long>(tabs.size()); |
35 |
restoredTabs.add(t.getId()); |
37 |
BackgroundHandler.execute(new PruneThumbnails(mActivity, restoredTabs)); |
38 |
if (tabs.size() == 0) { |
42 |
// TabControl.restoreState() will create a new tab even if |
43 |
// restoring the state fails. |
44 |
setActiveTab(mTabControl.getCurrentTab()); |
45 |
// Handle the intent if needed. If icicle != null, we are restoring |
46 |
// and the intent will be stale - ignore it. |
47 |
if (icicle == null || fromCrash) { |
48 |
mIntentHandler.onNewIntent(intent); |
51 |
// Read JavaScript flags if it exists. |
52 |
String jsFlags = getSettings().getJsEngineFlags(); |
53 |
if (jsFlags.trim().length() != 0) { |
54 |
getCurrentWebView().setJsFlags(jsFlags); |
57 |
if (BrowserActivity.ACTION_SHOW_BOOKMARKS.equals(intent.getAction())) { |
58 |
bookmarksOrHistoryPicker(ComboViews.Bookmarks); |
这里若是执行了else操做 会有个 恢复之前标签窗口的操做 其实就是从Bundle中拿到恢复的数据把窗口内容恢复: ide
发现最后仍是会请求网络来载入之前的网页, 这里也能够咱们自行改造, 在程序崩溃的时候把网页序列化到本地,在这里再读取到内存中.
02 |
* Restore the state of all the tabs. 恢复崩溃前的状态 |
03 |
* @param currentId The tab id to restore. |
04 |
* @param inState The saved state of all the tabs. |
05 |
* @param restoreIncognitoTabs Restoring private browsing tabs |
06 |
* @param restoreAll All webviews get restored, not just the current tab |
07 |
* (this does not override handling of incognito tabs) |
09 |
void restoreState(Bundle inState, long currentId, |
10 |
boolean restoreIncognitoTabs, boolean restoreAll) { |
11 |
if (currentId == -1) { |
14 |
long[] ids = inState.getLongArray(POSITIONS);//和saveState函数的put操做对应 |
15 |
long maxId = -Long.MAX_VALUE; |
16 |
HashMap<Long, Tab> tabMap = new HashMap<Long, Tab>(); |
17 |
for (long id : ids) {//这些崩溃以前保存的tab 对应的 id 这些tab都会恢复到多窗口栈中 |
21 |
final String idkey = Long.toString(id); |
22 |
Bundle state = inState.getBundle(idkey); |
23 |
if (state == null || state.isEmpty()) { |
26 |
} else if (!restoreIncognitoTabs |
27 |
&& state.getBoolean(Tab.INCOGNITO)) { |
29 |
} else if (id == currentId || restoreAll) { |
30 |
Tab t = createNewTab(state, false); |
32 |
// We could "break" at this point, but we want |
33 |
// sNextId to be set correctly. |
37 |
// Me must set the current tab before restoring the state |
38 |
// so that all the client classes are set. |
39 |
if (id == currentId) { |
40 |
setCurrentTab(t);//这是当前的tab |
43 |
// Create a new tab and don't restore the state yet, add it |
45 |
Tab t = new Tab(mController, state); |
48 |
// added the tab to the front as they are not current |
53 |
// make sure that there is no id overlap between the restored |
57 |
if (mCurrentTab == -1) { |
58 |
if (getTabCount() > 0) { |
59 |
setCurrentTab(getTab(0)); |
62 |
// restore parent/child relationships<span></span> for (long id : ids) { |
63 |
final Tab tab = tabMap.get(id); |
64 |
final Bundle b = inState.getBundle(Long.toString(id)); |
65 |
if ((b != null) && (tab != null)) { |
66 |
final long parentId = b.getLong(Tab.PARENTTAB, -1); |
68 |
final Tab parent = tabMap.get(parentId); |
70 |
parent.addChildTab(tab); |
说到这里了, 那么崩溃前的保存现场是在那里进行的呢?在 saveState
具体调用是:Activity的onSaveInstanceState转发到Controller Controller再转发到TabControl的saveState函数
02 |
* onSaveInstanceState(Bundle map) |
03 |
* onSaveInstanceState is called right before onStop(). The map contains |
07 |
protected void onSaveInstanceState(Bundle outState) { |
09 |
Log.v(LOGTAG, "BrowserActivity.onSaveInstanceState: this=" + this); |
11 |
mController.onSaveInstanceState(outState); |
02 |
* save the tab state: 保存崩溃前的状态 |
04 |
* position sorted array of tab ids |
05 |
* for each tab id, save the tab state |
09 |
void saveState(Bundle outState) { |
10 |
final int numTabs = getTabCount(); |
14 |
long[] ids = new long[numTabs]; |
16 |
for (Tab tab : mTabs) { |
17 |
Bundle tabState = tab.saveState(); |
18 |
if (tabState != null) { |
19 |
ids[i++] = tab.getId(); |
20 |
String key = Long.toString(tab.getId()); |
21 |
if (outState.containsKey(key)) { |
22 |
// Dump the tab state for debugging purposes |
23 |
for (Tab dt : mTabs) { |
24 |
Log.e(LOGTAG, dt.toString()); |
26 |
throw new IllegalStateException( |
27 |
"Error saving state, duplicate tab ids!"); |
29 |
outState.putBundle(key, tabState); |
32 |
// Since we won't be restoring the thumbnail, delete it |
33 |
tab.deleteThumbnail(); |
36 |
if (!outState.isEmpty()) { |
37 |
outState.putLongArray(POSITIONS, ids); |
38 |
Tab current = getCurrentTab(); |
40 |
if (current != null) { |
41 |
cid = current.getId(); |
43 |
outState.putLong(CURRENT, cid); |
因为并非全部tab都是能够恢复 (现场保存并不能保证所有都保存下来?)和须要恢复 (隐身窗口是不该该恢复) 的, 因此在onPreloginFinished 前 使用了canRestoreState函数进行判断:
02 |
* Check if the state can be restored. If the state can be restored, the |
03 |
* current tab id is returned. This can be passed to restoreState below |
04 |
* in order to restore the correct tab. Otherwise, -1 is returned and the |
05 |
* state cannot be restored. |
07 |
long canRestoreState(Bundle inState, boolean restoreIncognitoTabs) { |
08 |
final long[] ids = (inState == null) ? null : inState.getLongArray(POSITIONS); |
12 |
final long oldcurrent = inState.getLong(CURRENT); |
14 |
if (restoreIncognitoTabs || (hasState(oldcurrent, inState) && !isIncognito(oldcurrent, inState))) { |
17 |
// pick first non incognito tab |
19 |
if (hasState(id, inState) && !isIncognito(id, inState)) { |
28 |
private boolean hasState(long id, Bundle state) { |
29 |
if (id == -1) return false; |
30 |
Bundle tab = state.getBundle(Long.toString(id)); |
31 |
return ((tab != null) && !tab.isEmpty()); |
34 |
private boolean isIncognito(long id, Bundle state) { |
35 |
Bundle tabstate = state.getBundle(Long.toString(id)); |
36 |
if ((tabstate != null) && !tabstate.isEmpty()) { |
37 |
return tabstate.getBoolean(Tab.INCOGNITO); |