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


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

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

  • 相关阅读:
    SQL Server将一列的多行内容拼接成一行的问题讨论
    SQL 获取 IDENTITY 三种方法 SCOPE_IDENTITY、IDENT_CURRENT 和 @@IDENTITY的区别
    构建高性能服务(二)
    乐观锁解决高并发
    c#问答篇:对象与引用变量-----初学者的困惑
    vs调试 本地IIS
    【转】android adb常用指令
    【转】使用 JMeter 完成常用的压力测试
    【转】利用 Apache JMeter 测试 WebSphere 性能
    【转】用JMeter来测试Tomcat的性能
  • 原文地址:https://www.cnblogs.com/tonglingqijie/p/4710751.html
Copyright © 2020-2023  润新知