Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

wangbin0802/androidstockbrowser

Repository files navigation

Code Analysis of Android Stock Browser

This analysis aims to provide a thorough guide to developing browser apps based on stock browser.

Stock Browser for Android

代码分析

ControllerWebViewUIActivity的桥梁 启动界面BrowserActivity中最重要的方法是createController(),Controller被创建时会调用 mController.setUi(new PhoneUi(this, controller))

PhoneUi包含NavigationBarPhone
TitleBar包含NavigationBarPhone

BaseUi里关联一个custom_screen.xml布局,并会创建一个TitleBar custom_screen.xml里有一个idmain_contentFrameLayout,叫做mContentView, Tab切换都会从mContentView去添加和移除视图

<FrameLayout
	android:id="@+id/fixed_titlebar_container"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"/>
<FrameLayout
 android:id="@+id/main_content"
 android:layout_width="match_parent"
 android:layout_height="match_parent"/>

其中fixed_title_container是放置TitleBar的,参考BaseUi.addFixedTitleBar

重要的类

NavScreen是点击Tab Switch按钮后显示的Tab导航界面

加载URL流程

Controller.loadUrl -> Tab.loadUrl

Tab.loadUrl里会1. 设置正在加载URL状态标志;2. 调用WebViewController.onPageStarted;最后调用WebView.loadUrl

WebViewController.onPageStarted十分关键,会调用BaseUi.onTabDataChanged,然后会调用TitleBar.onTabDataChangedNavigationBarBase.onTabDataChanged

对菜单项的更新不在TitleBar.onTabDataChangedNavigationBarBase.onTabDataChanged中;

有关ControlleronPageStartedonPageFinished方法

有关Controller.onPageFinished是在Tab.mWebViewClient.onPageFinished中被回调;

分析"前进"、"后退"菜单项更新状态逻辑

这些菜单项是埋藏在"更多"菜单项里,每次点击该按钮,会创建、更新一个PopupMenu,所以每次加载网页都不会主动更新菜单状态,而是在点击"更多"菜单项时,才更新状态(Lazy Initialization)。

分析"Stop"按钮状态更新逻辑

在Tab加载网页时,会在TitleBar上显示一个Stop按钮,并在加载完后隐藏。Stop按钮作为控件状态更新的典型,厘清该控制流程非常重要。

  1. 首先发现该按钮位于NavigationBarPhone内;
  2. 发现在更新Stop按钮的状态位于onProgressStartedonProgressStoppedonStateChanged中;

UrlInputView使用了Observer设计模式,其为一个主题(Subject)对象,而NavigationBarPhone为观察者,彼此通过UrlInputView.StateListener.StateListener接口来进行通信,其中NavigationBarPhone实现UrlInputView.StateListener接口。

NavigationBarPhoneonProgressStartedonProgressStopped被回调的地方是TitleBar.setProgress,而TitleBar.setProgress是被BaseUi.onProgressChanged调用的;

分析Back逻辑

BrowserActivity将onKeyDown和onKeyUp方法的处理委托给Controller的onKeyDown和onKeyUp。当按下返回键,Controller.onBackKey会被回调:

protected void onBackKey() {
 if (!mUi.onBackKey()) {
 WebView subwindow = mTabControl.getCurrentSubWindow();
 if (subwindow != null) {
 if (subwindow.canGoBack()) {
 subwindow.goBack();
 } else {
 dismissSubWindow(mTabControl.getCurrentTab());
 }
 } else {
 goBackOnePageOrQuit();
 }
 }
}

可见,Controller先将处理委托给UI.onBackKey,仅当返回false时,Controller会让当前的SubWindow来消化该事件,如果没有SubWindow,则由Controller.goBackOnePageOrQuit处理该事件。

当当前Tab不为null,并且无法goBack时,如果有parent则切换到parent后再关闭tab,如果没有则关闭当前Tab,即调用Controller.closeCurrentTab

NavScreen代码分析

NavScreen是多标签切换界面,它的布局是nav_screen.xml。 布局中重要的类是NavTabScroller,它主要负责Tab页滚动。 ViewConfiguration.get(Context).hasPermanentMenuKey()判断是否有固态Menu键 NavTabScoller通过设置BaseAdapter来绑定数据

内存释放

BrowserActivity.onLowMemory会委托Controller.onLowMemory释放内存。

@Override
public void onLowMemory() {
 super.onLowMemory();
 mController.onLowMemory();
}

Controller.onLowMemory会调用mTabControl.freeMemory来释放内存:

/**
 * Free the memory in this order, 1) free the background tabs; 2) free the
 * WebView cache;
 */
void freeMemory() {
 if (getTabCount() == 0) return;
 // free the least frequently used background tabs
 Vector<Tab> tabs = getHalfLeastUsedTabs(getCurrentTab());
 if (tabs.size() > 0) {
 Log.w(LOGTAG, "Free " + tabs.size() + " tabs in the browser");
 for (Tab t : tabs) {
 // store the WebView's state.
 t.saveState();
 // destroy the tab
 t.destroy();
 }
 return;
 }
 // free the WebView's unused memory (this includes the cache)
 Log.w(LOGTAG, "Free WebView's unused memory and cache");
 WebView view = getCurrentWebView();
 if (view != null) {
 view.freeMemory();
 }
}

问题是,该方法并不能实质释放内存,至少在小米4c上调试时观察Android Monitor的Memory曲线,并无内存释放。

释放内存的步骤为,先释放后台Tabs,再释放当前WebView的内存缓存。

网址重定向的分析

当发生网址重定向时,WebView会多次回调WebViewClient.onPageStarted方法。比如在地址栏输入qq.com后,回调方式如下:

V/Tab: ⇢ onPageStarted(view=BrowserWebView, url="http://qq.com/", favicon=Bitmap@23b4987f)
V/Tab: ⇢ onPageStarted(view=BrowserWebView, url="http://www.qq.com/", favicon=Bitmap@23b4987f)
V/Tab: ⇢ onPageStarted(view=BrowserWebView, url="http://xw.qq.com/index.htm", favicon=Bitmap@23b4987f)
V/Tab: ⇢ onReceivedIcon(view=BrowserWebView, icon=Bitmap@315bd90a)
V/Tab: ⇢ onPageFinished(view=BrowserWebView, url="http://xw.qq.com/index.htm")

可见每重定向一次,就会回调onPageStarted方法,倘若需要多次重定向,则每次都会回调onPageStarted方法。只有最终重定向的网址会被记录到历史栈中。

获取Favicon分析

对一个标签页,获取Favicon时机有:

  1. Tab$mWebViewClient.onPageStarted
  2. Tab$mWebViewClient.onReceivedIcon

值得注意的是,这两个回调返回的Bitmap对象并不是同一个。那么这两个方法返回的favicon各是什么呢?

onPageStarted()返回上一个网页的favicon;而onReceivedIcon返回当前网页的favicon。

假设先访问A(qq.com),onPageStarted返回Favicon A1,onReceivedIcon返回Favicon A2,接着访问B,B是没有Favicon的站点(比如mail.126.com),onPageStarted返回Favicon A2。

获取标题分析

WebChromeClient.onReceivedTitle(WebView view, String title)提供了从WebView获取标题的方法。 一般加载一个网页时,onReceivedTitle会在onPageStartedonPageFinished之间被回调,但是调用goBack回到上一个网页,onReceivedTitle却不会被回调。

TitleBar分析

TitleBar里带有刷/暂停按钮,这个按钮的状态变化将是接下来分析的重点

About

Stock Browser for Android

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Java 100.0%

AltStyle によって変換されたページ (->オリジナル) /