Android Browser学习四 多窗口 初始化

整理了一下Browser系列, 以前写的太乱了.

Tab是浏览器和用户打交道的主要UI,浏览器的最主要的功能--上网就是有他来完成了.上一篇文章我们已经看到了

BrowserActivity是如何展现的第一个Tab, 这里我们看一下Tab的初始化:

TabControl是管理整个窗口切换的逻辑, 我们打开浏览器标签窗口, 切换窗口, 关闭窗口, 都是通过TabControl实现的.可见TabControl是一个非常重要的部件

在Controller中, 首先初始化的也是TabControl, 其构造函数如下:



?
1
2
3
4
5
6
7
8
9
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


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     }



01 //开始
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.
05  
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);
11  
12         final boolean restoreIncognitoTabs = !(lastActiveDate == null
13             || lastActiveDate.before(yesterday) //当天启动的才会恢复以前的页面
14             || lastActiveDate.after(today));
15  
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);
20  
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();
26         }
27  
28         GoogleAccountLogin.startLoginIfNeeded(mActivity,//登陆谷歌账户
29                 new Runnable() {
30                     @Override public void run() {
31                         //登陆之后在处理一下其他操作
32                         onPreloginFinished(icicle, intent, currentTabId, restoreIncognitoTabs,
33                                 fromCrash);
34                     }
35                 });
36     }

在这里有个获取 需要恢复窗口id的操作  final long currentTabId = mTabControl.canRestoreState(icicle, restoreIncognitoTabs);


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.
06      */
07     long canRestoreState(Bundle inState, boolean restoreIncognitoTabs) {
08         final long[] ids = (inState == null) ? null : inState.getLongArray(POSITIONS);
09         if (ids == null) {
10             return -1;
11         }
12         final long oldcurrent = inState.getLong(CURRENT);
13         long current = -1;
14         if (restoreIncognitoTabs || (hasState(oldcurrent, inState) && !isIncognito(oldcurrent, inState))) {
15             current = oldcurrent;
16         } else {
17             // pick first non incognito tab
18             for (long id : ids) {
19                 if (hasState(id, inState) && !isIncognito(id, inState)) {
20                     current = id;
21                     break;
22                 }
23             }
24         }
25         return current;
26     }
27  
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());
32     }
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);
38         }
39         return false;
40     }


后面的代码其实还是在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         } 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());
34             for (Tab t : tabs) {
35                 restoredTabs.add(t.getId());
36             }
37             BackgroundHandler.execute(new PruneThumbnails(mActivity, restoredTabs));
38             if (tabs.size() == 0) {
39                 openTabToHomePage();
40             }
41             mUi.updateTabs(tabs);
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);
49             }
50         }
51         // Read JavaScript flags if it exists.
52         String jsFlags = getSettings().getJsEngineFlags();
53         if (jsFlags.trim().length() != 0) {
54             getCurrentWebView().setJsFlags(jsFlags);
55         }
56         //是否是选取bookmarks
57         if (BrowserActivity.ACTION_SHOW_BOOKMARKS.equals(intent.getAction())) {
58             bookmarksOrHistoryPicker(ComboViews.Bookmarks);
59         }
60     }


这里如果执行了else操作 会有个 恢复以前标签窗口的操作 其实就是从Bundle中拿到恢复的数据把窗口内容恢复:

发现最后还是会请求网络来载入以前的网页, 这里也可以我们自行改造, 在程序崩溃的时候把网页序列化到本地,在这里再读取到内存中.

01 /**
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)
08     */
09    void restoreState(Bundle inState, long currentId,
10            boolean restoreIncognitoTabs, boolean restoreAll) {
11        if (currentId == -1) {
12            return;
13        }
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都会恢复到多窗口栈中
18            if (id > maxId) {
19                maxId = id;
20            }
21            final String idkey = Long.toString(id);
22            Bundle state = inState.getBundle(idkey);
23            if (state == null || state.isEmpty()) {
24                // Skip tab
25                continue;
26            } else if (!restoreIncognitoTabs
27                    && state.getBoolean(Tab.INCOGNITO)) {
28                // ignore tab
29            } else if (id == currentId || restoreAll) {
30                Tab t = createNewTab(state, false);
31                if (t == null) {
32                    // We could "break" at this point, but we want
33                    // sNextId to be set correctly.
34                    continue;
35                }
36                tabMap.put(id, t);
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
41                }
42            } else {
43                // Create a new tab and don't restore the state yet, add it
44                // to the tab list
45                Tab t = new Tab(mController, state);
46                tabMap.put(id, t);
47                mTabs.add(t);
48                // added the tab to the front as they are not current
49                mTabQueue.add(0, t);
50            }
51        }
52  
53        // make sure that there is no id overlap between the restored
54        // and new tabs
55        sNextId = maxId + 1;
56  
57        if (mCurrentTab == -1) {
58            if (getTabCount() > 0) {
59                setCurrentTab(getTab(0));
60            }
61        }
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);
67                if (parentId != -1) {
68                    final Tab parent = tabMap.get(parentId);
69                    if (parent != null) {
70                        parent.addChildTab(tab);
71                    }
72                }
73            }
74        }
75    }

说到这里了, 那么崩溃前的保存现场是在那里进行的呢?在 saveState

具体调用是:Activity的onSaveInstanceState转发到Controller Controller再转发到TabControl的saveState函数


01 /**
02      *  onSaveInstanceState(Bundle map)
03      *  onSaveInstanceState is called right before onStop(). The map contains
04      *  the saved state.
05      */
06     @Override
07     protected void onSaveInstanceState(Bundle outState) {
08         if (LOGV_ENABLED) {
09             Log.v(LOGTAG, "BrowserActivity.onSaveInstanceState: this=" + this);
10         }
11         mController.onSaveInstanceState(outState);
12     }





01 /**
02      * save the tab state: 保存崩溃前的状态
03      * current position
04      * position sorted array of tab ids
05      * for each tab id, save the tab state
06      * @param outState
07      * @param saveImages
08      */
09     void saveState(Bundle outState) {
10         final int numTabs = getTabCount();
11         if (numTabs == 0) {
12             return;
13         }
14         long[] ids = new long[numTabs];
15         int i = 0;
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());
25                     }
26                     throw new IllegalStateException(
27                             "Error saving state, duplicate tab ids!");
28                 }
29                 outState.putBundle(key, tabState);
30             } else {
31                 ids[i++] = -1;
32                 // Since we won't be restoring the thumbnail, delete it
33                 tab.deleteThumbnail();
34             }
35         }
36         if (!outState.isEmpty()) {
37             outState.putLongArray(POSITIONS, ids);
38             Tab current = getCurrentTab();
39             long cid = -1;
40             if (current != null) {
41                 cid = current.getId();
42             }
43             outState.putLong(CURRENT, cid);
44         }
45     }



由于并不是所有tab都是可以恢复 (现场保存并不能保证全部都保存下来?)和需要恢复 (隐身窗口是不应该恢复) 的, 所以在onPreloginFinished 前 使用了canRestoreState函数进行判断:



01 /** 
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.
06    */
07   long canRestoreState(Bundle inState, boolean restoreIncognitoTabs) {
08       final long[] ids = (inState == null) ? null : inState.getLongArray(POSITIONS);
09       if (ids == null) {
10           return -1;
11       }
12       final long oldcurrent = inState.getLong(CURRENT);
13       long current = -1;
14       if (restoreIncognitoTabs || (hasState(oldcurrent, inState) && !isIncognito(oldcurrent, inState))) {
15           current = oldcurrent;
16       } else {
17           // pick first non incognito tab
18           for (long id : ids) {
19               if (hasState(id, inState) && !isIncognito(id, inState)) {
20                   current = id;
21                   break;
22               }
23           }
24       }
25       return current;
26   }
27  
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());
32   }
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);
38       }
39       return false;
40   }

原文地址:http://my.oschina.net/sfshine/blog/197804
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值