文档章节

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

SuShine
 SuShine
发布于 2014/02/08 15:19
字数 2307
阅读 5319
收藏 7

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的学习


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

© 著作权归作者所有

SuShine
粉丝 126
博文 574
码字总数 157991
作品 0
朝阳
后端工程师
私信 提问
Android 浏览器的研究(三)--- APK, Activity, Controller和Ui

从浏览器Apk的AndroidManifest.xml文件看到,Apk的Application 类和主Activity类分别是 Browser和BrowserActivity。 Browser Application类在onCreate方法中进行了以下工作: 1. 同步Cookie ...

孙洪波
2014/01/24
0
0
Android 浏览器的研究(四)--- Apk的启动和主页的加载过程

当我们在Launcher中点击浏览器的图标时,浏览器的窗口会打开并显示主页(HomePage)。这里我们对这一场景进行分析,研究浏览器如何启动,取得缺省主页并将它布局和显示的。 根据前边对WebVi...

孙洪波
2014/02/07
0
0
App启动时间测试

命令方式: adb shell am start -W -n com.android.browser/.BrowserActivity(回车后观察模拟器) -W 是指启动完成之后,返回启动耗时 -n 后面是需要启动的App的包名和launchActivity this...

爱与梦想
2017/09/23
0
0
添加Flutter到现有Android的项目

原文链接 https://tryenough.com/flutter02 添加Flutter到现有Android的项目 $ flutter create -t module my_flutter $ ./gradlew flutter:assembleDebug setBinding(new Binding([gradle: ......

TryEnough
01/04
0
0
深入理解Android系列书籍的规划路线图

深入理解Android系列书籍的规划路线图 一 Roadmap “深入理解Android“书籍从卷I推出以后就受到广大读者的喜爱。在和读者交流的过程中,笔者被问及最多的一个问题就是,卷II什么时候推出?内...

邓凡平
2012/06/09
0
40

没有更多内容

加载失败,请刷新页面

加载更多

如何在工作中快速成长?致工程师的10个简单技巧

阿里妹导读:阿里有句非常经典的土话,“今天的最好表现,是明天的最低要求。”如何挖掘潜能、发现更好的自己?今天,阿里巴巴高级无线开发专家江建明将认知升级的方法总结出来,帮助你获得快...

阿里云云栖社区
35分钟前
1
0
PHP和Redis实现在高并发下的抢购及秒杀功能

抢购、秒杀是平常很常见的场景,面试的时候面试官也经常会问到,比如问你淘宝中的抢购秒杀是怎么实现的等等。 抢购、秒杀实现很简单,但是有些问题需要解决,主要针对两个问题: 一、高并发对...

xiaogg
37分钟前
1
0
从数据上看:谁才是漫威的绝对C位

复联4上映了!这次比美国还早了两天。当然,我还没看,不会给你们剧透,当然也不想不剧透。 这一部不仅是灭霸这一线剧情的结局,也被认为漫威第三阶段的收官之作。据说此部之后,不少影迷熟知...

crossin
50分钟前
4
0
Spring Cloud底层原理

毫无疑问,Spring Cloud 是目前微服务架构领域的翘楚,无数的书籍博客都在讲解这个技术。 不过大多数讲解还停留在对 Spring Cloud 功能使用的层面,其底层的很多原理,很多人可能并不知晓。 ...

月下狼
今天
8
0
Linux重启Tomcat

在测试过程中,要构建测试环境,还经常要重启Tomcat排查问题,重启Tomcat的步骤: 1、首先查看Tomcat是否有启动或重复启动? 输入命令ps -aux|grep java按回车键,可见下图,是有一个Tomcat启...

测试龙管家
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部