一、简介
为了方便开发者实现在app内展示网页并与网页交互的需求,Android SDK提供了WebView组件。它继承自AbsoluteLayout,展示网页的同时,也可以在其中放入其他的子View。现如今,Hybrid应用似乎占据的APP的主流类型,那么关于WebView的使用就变得越发的重要。从Android 4.4(KitKat)开始,原本基于WebKit的WebView开始基于Chromium内核,这一改动大大提升了WebView组件的性能以及对HTML5,CSS3,JavaScript的支持。
二、相关类及API
1、WebView
基本:
-
String getUrl()
:获取当前页面的URL。 -
String getTitle()
:获取当前页面的标题。 -
Bitmap getFavicon()
:获取当前页面的favicon -
int getProgress()
:获取当前页面的加载进度 -
setInitialScale(int scaleInPercent)
:设置初始缩放比例
加载网页:
-
loadUrl(String url)
:加载URL指定的网页 -
loadUrl(String url, Map<String, String> additionalHttpHeaders)
:携带http headers加载URL指定的网页 -
postUrl(String url, byte[] postData)
:使用POST请求加载指定的网页 -
reload()
:重新reload当前的URL,即刷新。 -
loadData(String data, String mimeType, String encoding)
:加载内容 -
loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)
:使用baseUrl加载内容
导航:
-
boolean canGoBack()
:用来确认WebView里是否还有可回退的历史记录。通常我们会在WebView里重写返回键的点击事件,通过该方法判断WebView里是否还有历史记录,若有则返回上一页。 -
boolean canGoForward()
:用来确认WebView是否还有可向前的历史记录。 -
boolean canGoBackOrForward(int steps)
:以当前的页面为起始点,用来确认WebView的历史记录是否足以后退或前进给定的步数,正数为前进,负数为后退。 -
goBack()
:在WebView历史记录后退到上一项。 -
goForward()
:在WebView历史记录里前进到下一项。 -
goBackOrForward(int steps)
:以当前页面为起始点,前进或后退历史记录中指定的步数,正数为前进,负数为后退。 -
WebBackForwardList copyBackForwardList()
:复制一份BackForwardList -
clearHistory()
:清除当前webview访问的历史记录。
JavaScript:
-
addJavascriptInterface(Object object, String name)
:注入Javascript对象 -
removeJavascriptInterface(String name)
:移除已注入的Javascript对象,下次加载或刷新页面时生效 -
evaluateJavascript(String script, ValueCallback<String> resultCallback)
:对传入的JS表达式求值,通过resultCallback返回结果。此函数添加于API19,必须在UI线程中调用,回调也将在UI线程
其他:
-
findAllAsync (String find)
:异步执行查找网页内包含的字符串并设置高亮,查找结果会回调 -
findNext (boolean forward)
: 查找下一个匹配的字符串 -
setFindListener(FindListener listener)
:设置网页查找结果回调 -
clearMatches()
:清除网页查找的高亮匹配字符串 -
saveWebArchive(String filename)
:保存网页(.html)到指定文件 -
saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback)
:保存网页(.html)到文件 -
pageUp(boolean top)
:将WebView展示的页面滑动至顶部。 -
pageDown(boolean bottom)
:将WebView展示的页面滑动至底部。 -
zoomBy(float factor)
:缩放 -
zoomIn()
:放大 -
zoomOut()
:缩放 -
clearCache(boolean includeDiskFiles)
:清空网页访问留下的缓存数据。需要注意的时,由于缓存是全局的,所以只要是WebView用到的缓存都会被清空,即便其他地方也会使用到。该方法接受一个参数,从命名即可看出作用。若设为false,则只清空内存里的资源缓存,而不清空磁盘里的。 -
clearFormData()
:清除自动完成填充的表单数据。需要注意的是,该方法仅仅清除当前表单域自动完成填充的表单数据,并不会清除WebView存储到本地的数据。 -
clearSslPreferences()
:清除SSL偏好 -
onPause()
:当页面被失去焦点被切换到后台不可见状态,需要执行onPause操作,该操作会通知内核安全地暂停所有动作,比如动画的执行或定位的获取等。需要注意的是该方法并不会暂停JavaScript的执行,若要暂停JavaScript的执行请使用接下来的这个方法。 -
onResume()
:在先前调用onPause()后,我们可以调用该方法来恢复WebView的运行。 -
pauseTimers()
:该方法面向全局整个应用程序的webview,它会暂停所有webview的layout,parsing,JavaScript Timer。当程序进入后台时,该方法的调用可以降低CPU功耗。 -
resumeTimers()
:恢复pauseTimers时的所有操作。 -
destroy()
:销毁WebView。需要注意的是:这个方法的调用应在WebView从父容器中被remove掉之后。我们可以手动地调用 -
getContentHeight()
:该方法返回整个HTML页面的高度,但该高度值并不等同于当前整个页面的高度,因为WebView有缩放功能, 所以当前整个页面的高度实际上应该是原始HTML的高度再乘上缩放比例
2、WebSettings
-
setDomStorageEnabled(boolean flag)
:启用HTML5 DOM storage API,默认值 false -
setDatabaseEnabled(boolean flag)
:启用Web SQL Database API,这个设置会影响同一进程内的所有WebView,默认值 false,此API已不推荐使用 -
setAppCacheEnabled(boolean flag)
:启用Application Caches API,必需设置有效的缓存路径才能生效,默认值 false,此API已废弃 -
setAppCachePath(String appCachePath)
:设值缓存路径 -
setGeolocationEnabled(boolean flag)
:启用定位 -
setSaveFormData(boolean save)
:是否保存表单数据 -
setNeedInitialFocus(boolean flag)
:是否当webview调用requestFocus时为页面的某个元素设置焦点,默认值 true -
setUseWideViewPort(boolean use)
:是否支持viewport属性,默认值 false, 页面通过<meta name="viewport" ... />
自适应手机屏幕 -
setLoadWithOverviewMode(boolean overview)
:是否使用overview mode加载页面,默认值 false,当页面宽度大于WebView宽度时,缩小使页面宽度等于WebView宽度 -
setLayoutAlgorithm(LayoutAlgorithm l)
:布局算法,默认是LayoutAlgorithm#NARROW_COLUMNS -
setJavaScriptEnabled(boolean flag)
:是否支持Javascript,默认值false -
setSupportMultipleWindows(boolean support)
:是否支持多窗口,默认值false -
setJavaScriptCanOpenWindowsAutomatically(boolean flag)
:是否可用Javascript(window.open)打开窗口,默认值 false -
setAllowContentAccess(boolean allow)
:/是否可访问Content Provider的资源,默认值 true -
setAllowFileAccess(boolean allow)
:是否可访问本地文件,默认值 true -
setAllowFileAccessFromFileURLs(boolean flag)
:是否允许通过file url加载的Javascript读取本地文件,默认值 false -
setAllowUniversalAccessFromFileURLs(boolean flag)
:是否允许通过file url加载的Javascript读取全部资源(包括文件,http,https),默认值 false -
setLoadsImagesAutomatically(boolean flag)
:是否自动加载图片 -
setBlockNetworkImage(boolean flag)
:禁止加载网络图片 -
setBlockNetworkLoads(boolean flag)
:禁止加载所有网络资源 -
setSupportZoom(boolean support)
:是否支持缩放 -
setBuiltInZoomControls(boolean enabled)
:是否使用内置缩放机制 -
setDisplayZoomControls(boolean enabled)
:是否显示内置缩放控件 -
setDefaultTextEncodingName(String encoding)
:设置文本编码 -
setDefaultFontSize(int size)
:设置默认字体大小 -
setDefaultFixedFontSize(int size)
:默认等宽字体尺寸 -
setMinimumFontSize(int size)
:最小文字尺寸,默认值 8 -
setMinimumLogicalFontSize(int size)
:最小文字逻辑尺寸,默认值 8 -
setTextZoom(int textZoom)
:文字缩放百分比,默认值 100 -
setStandardFontFamily(String font)
:标准字体,默认值 "sans-serif" -
setSerifFontFamily(String font)
:衬线字体,默认值 "serif" -
setSansSerifFontFamily(String font)
:无衬线字体,默认值 "sans-serif" -
setFixedFontFamily(String font)
:等宽字体,默认值 "monospace" -
setCursiveFontFamily(String font)
:手写体,默认值 "cursive" -
setFantasyFontFamily(String font)
:幻想体,默认值 "fantasy" -
setMediaPlaybackRequiresUserGesture(boolean require)
:用户是否需要通过手势播放媒体(不会自动播放),默认值 true -
setOffscreenPreRaster(boolean enabled)
:是否在离开屏幕时光栅化(会增加内存消耗),默认值 false -
setCacheMode(@CacheMode int mode)
:用来设置WebView的缓存模式。当我们加载页面或从上一个页面返回的时候,会按照设置的缓存模式去检查并使用(或不使用)缓存。
缓存模式有四种:- LOAD_DEFAULT:默认的缓存使用模式。在进行页面前进或后退的操作时,如果缓存可用并未过期就优先加载缓存,否则从网络上加载数据。这样可以减少页面的网络请求次数。
- LOAD_CACHE_ELSE_NETWORK:只要缓存可用就加载缓存,哪怕它们已经过期失效。如果缓存不可用就从网络上加载数据。
- LOAD_NO_CACHE:不加载缓存,只从网络加载数据。
- LOAD_CACHE_ONLY:不从网络加载数据,只从缓存加载数据。
通常我们可以根据网络情况将这几种模式结合使用,比如有网的时候使用LOAD_DEFAULT,离线时使用LOAD_CACHE_ONLY、LOAD_CACHE_ELSE_NETWORK,让用户不至于在离线时啥都看不到
3、WebViewClient
从名字上不难理解,这个类就像WebView的委托人一样,是帮助WebView处理各种通知和请求事件的
public boolean shouldOverrideUrlLoading(WebView view, String url)
拦截页面加载,返回true表示宿主app拦截并处理了该url,否则返回false由当前WebView处理
此方法在API24被废弃,不处理POST请求public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
拦截页面加载,返回true表示宿主app拦截并处理了该url,否则返回false由当前WebView处理
此方法添加于API24,不处理POST请求,可拦截处理子frame的非http请求public WebResourceResponse shouldInterceptRequest(WebView view, String url)
此方法废弃于API21,调用于非UI线程拦截资源请求并返回响应数据,返回null时WebView将继续加载资源。注意:API21以下的AJAX请求会走onLoadResource,无法通过此方法拦截public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request)
此方法添加于API21,调用于非UI线程,拦截资源请求并返回数据,返回null时WebView将继续加载资源public void onPageStarted(WebView view, String url, Bitmap favicon)
页面(url)开始加载public void onPageFinished(WebView view, String url)
页面(url)完成加载public void onLoadResource(WebView view, String url)
将要加载资源(url)public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
此方法废弃于API23, 主框架加载资源时出错public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error)
此方法添加于API23,加载资源时出错,通常意味着连接不到服务器。由于所有资源加载错误都会调用此方法,所以此方法应尽量逻辑简单public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse)
此方法添加于API23, 在加载资源(iframe,image,js,css,ajax...)时收到了 HTTP 错误(状态码>=400)public void onFormResubmission(WebView view, Message dontResend, Message resend)
是否重新提交表单,默认不重发public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)
加载资源时发生了一个SSL错误,应用必需响应(继续请求或取消请求), 处理决策可能被缓存用于后续的请求,默认行为是取消请求public void onReceivedClientCertRequest(WebView view, ClientCertRequest request)
此方法添加于API21,在UI线程被调用, 处理SSL客户端证书请求,必要的话可显示一个UI来提供KEY。
有三种响应方式:proceed()/cancel()/ignore(),默认行为是取消请求
如果调用proceed()或cancel(),Webview 将在内存中保存响应结果且对相同的"host:port"不会再次调用 onReceivedClientCertRequest
多数情况下,可通过KeyChain.choosePrivateKeyAlias启动一个Activity供用户选择合适的私钥public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm)
处理HTTP认证请求,默认行为是取消请求public void onReceivedLoginRequest(WebView view, String realm, String account, String args)
通知应用有个已授权账号自动登陆了public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event)
给应用一个机会处理按键事件,如果返回true,WebView不处理该事件,否则WebView会一直处理,默认返回falsepublic void onScaleChanged(WebView view, float oldScale, float newScale)
通知应用页面缩放系数变化
一些关键方法调用流程:
- loadUrl()无重定向时
onPageStarted->onPageFinished
- loadUrl()网页A重定向到B时
onPageStarted->shouldOverrideUrlLoading->onPageStarted->onPageFinished->onPageFinished
- 在已加载的页面中点击链接,加载页面A(无重定向)
shouldOverrideUrlLoading->onPageStarted->onPageFinished
- 在已加载的页面中点击链接,加载页面A(页面A重定向至页面B)
shouldOverrideUrlLoading->onPageStarted->shouldOverrideUrlLoading->onPageStarted->onPageFinished->onPageFinished
- 执行goBack/goForward/reload方法
onPageStarted->onPageFinished
- 发生资源加载
shouldInterceptRequest->onLoadResource
- 页面加载
shouldOverrideUrlLoading
onProgressChanged[10]
shouldInterceptRequest
onProgressChanged[...]
onPageStarted
onProgressChanged[...]
onLoadResource
onProgressChanged[...]
onReceivedTitle/onPageCommitVisible
onProgressChanged[100]
onPageFinished
onReceivedIcon
4、WebChromeClient
public void getVisitedHistory(ValueCallback<String[]> callback)
获得所有访问历史项目的列表,用于链接着色。public Bitmap getDefaultVideoPoster()
<video /> 控件在未播放时,会展示为一张海报图,HTML中可通过它的'poster'属性来指定。
如果未指定'poster'属性,则通过此方法提供一个默认的海报图。public View getVideoLoadingProgressView()
当全屏的视频正在缓冲时,此方法返回一个占位视图(比如旋转的菊花)。public void onProgressChanged(WebView view, int newProgress)
接收当前页面的加载进度public void onReceivedTitle(WebView view, String title)
接收文档标题public void onReceivedIcon(WebView view, Bitmap icon)
接收图标(favicon)public void onShowCustomView(View view, CustomViewCallback callback)
通知应用当前页进入了全屏模式,此时应用必须显示一个包含网页内容的自定义Viewpublic void onHideCustomView()
通知应用当前页退出了全屏模式,此时应用必须隐藏之前显示的自定义Viewpublic boolean onJsAlert(WebView view, String url, String message, JsResult result)
显示一个alert对话框public boolean onJsConfirm(WebView view, String url, String message, JsResult result)
显示一个confirm对话框public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result)
显示一个prompt对话框public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result)
显示一个对话框让用户选择是否离开当前页面public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback)
指定源的网页内容在没有设置权限状态下尝试使用地理位置API。
从API24开始,此方法只为安全的源(https)调用,非安全的源会被自动拒绝public void onGeolocationPermissionsHidePrompt()
当前一个调用 onGeolocationPermissionsShowPrompt() 取消时,隐藏相关的UI。public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg)
通知应用打开新窗口public void onCloseWindow(WebView window)
通知应用关闭窗口public void onRequestFocus(WebView view)
请求获取取焦点public void onPermissionRequest(PermissionRequest request)
通知应用网页内容申请访问指定资源的权限(该权限未被授权或拒绝)public void onPermissionRequestCanceled(PermissionRequest request)
通知应用权限的申请被取消,隐藏相关的UI。public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams)
为'<input type="file" />'显示文件选择器,返回false使用默认处理public boolean onConsoleMessage(ConsoleMessage consoleMessage)
接收JavaScript控制台消息
5、CookieManager
-
CookieManager getInstance()
:获得一个CookieManager实例 -
String getCookie(String url)
:根据url获取Cookie,以字符串形式返回Cookie -
void setCookie(String url, String value)
:为url设置Cookie -
void setCookie(String url, String value, ValueCallback<Boolean> callback)
:
callback的onReceiveValue方法获取的参数如果是true,代表本次设置成功,否则代表设置失败。如果并不关心执行结果,为callback参数传入null即可 -
void setAcceptCookie(boolean accept)
:设置WebView是否允许使用Cookie,这个方法针对的是当前应用的所有WebView。 -
void setAcceptThirdPartyCookies(WebView webview, boolean accept)
:设置WebView是否允许设置第三方Cookie,Android 5.0(API 21)以下默认为true,Android 5.0及以上默认为false -
void removeSessionCookie()
:移除所有Session Cookies ,Android 5.0(API 21)已弃用 -
void removeSessionCookies(ValueCallback<Boolean> callback)
:移除所有Session Cookies(异步执行),在执行完移除操作后,会回调onReceiveValue方法 -
void removeAllCookie()
:移除所有Cookies ,Android 5.0(API 21)已弃用 -
void removeAllCookies(ValueCallback<Boolean> callback)
:移除所有Cookies(异步执行) -
boolean hasCookies()
:判断是否存在Cookies -
void removeExpiredCookie()
:移除过期的Cookies
三、特殊场景介绍
1、视口(viewport)
视口是一个为网页提供绘图区域的矩形。
你可以指定数个视口属性,比如尺寸和初始缩放系数(initial scale)。其中最重要的是视口宽度,它定义了网页水平方向的可用像素总数(可用的CSS像素数)。
多数 Android 上的网页浏览器(包括 Chrome)设置默认视口为一个大尺寸(被称为"wide viewport mode",宽约 980px)。
也有许多浏览器默认会尽可能缩小以显示完整的视口宽度(被称为"overview mode")
// 是否支持viewport属性,默认值 false
// 页面通过`<meta name="viewport" ... />`自适应手机屏幕
// 当值为true且viewport标签不存在或未指定宽度时使用 wide viewport mode
settings.setUseWideViewPort(true);
// 是否使用overview mode加载页面,默认值 false
// 当页面宽度大于WebView宽度时,缩小使页面宽度等于WebView宽度
settings.setLoadWithOverviewMode(true);
viewport 语法
<meta name="viewport"
content="
height = [pixel_value | "device-height"] ,
width = [pixel_value | "device-width"] ,
initial-scale = float_value ,
minimum-scale = float_value ,
maximum-scale = float_value ,
user-scalable = ["yes" | "no"]
" />
通过WebView设置初始缩放(initial-scale)
// 设置初始缩放百分比
// 0表示依赖于setUseWideViewPort和setLoadWithOverviewMode
// 100表示不缩放
web.setInitialScale(0)
2、长按保存图片或者拨打电话
一般浏览器都有长按保存图片或者拨打图片的功能,实现这个功能和WebView.HitTestResult这个类有关,这个类会将你触摸网页的地方的类型和其他信息反馈给你。
WebView.HitTestResult的常用方法:
- HitTestResult.getExtra():获取额外的信息。
- HitTestResult.getType():获取所选中目标的类型,可以是图片,超链接,邮件,电话等等。
WebView.HitTestResult.UNKNOWN_TYPE //未知类型 WebView.HitTestResult.PHONE_TYPE //电话类型 WebView.HitTestResult.EMAIL_TYPE //电子邮件类型 WebView.HitTestResult.GEO_TYPE //地图类型 WebView.HitTestResult.SRC_ANCHOR_TYPE //超链接类型 WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE //带有链接的图片类型 WebView.HitTestResult.IMAGE_TYPE //单纯的图片类型 WebView.HitTestResult.EDIT_TEXT_TYPE //选中的文字类型
- 给WebView设置长按监听事件;
- 获取WebView长按时的WebView.HitTestResult的事件类型,如果是图片,则做处理。
webView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
WebView.HitTestResult result = ((WebView) view).getHitTestResult();
if(result != null){
switch (result.getType()){
case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
String imgUrl = result.getExtra();
...
return true;
...
}
}
return false;
}
});
3、与Javascript交互
启用Javascript
// 是否支持Javascript,默认值false
settings.setJavaScriptEnabled(true);
注入对象到Javascript
// 注入对象'jsobj',在网页中通过`jsobj.say(...)`调用
web.addJavascriptInterface(new JSObject(), "jsobj")
在API17后支持白名单,只有添加了@JavascriptInterface注解的方法才会注入JS
public class JSObject {
@JavascriptInterface
public void say(String words) {
// todo
}
}
移除已注入Javascript的对象
web.removeJavascriptInterface("jsobj")
执行JS表达式
// 弹出提示框
web.loadUrl("javascript:alert('hello')");
// 调用注入的jsobj.say方法
web.loadUrl("javascript:jsobj.say('hello')");
在API19后可异步执行JS表达式,并通过回调返回值
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
vWeb.evaluateJavascript("111+222", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
// value => "333"
}
});
}
4、全屏(Fullscreen)
- 当H5请求全屏时,会回调 WebChromeClient.onShowCustomView 方法
- 当H5退出全屏时,会回调 WebChromeClient.onHideCustomView 方法
- manifest
自己处理屏幕尺寸方向的变化(切换屏幕方向时不重建activity)
WebView播放视频需要开启硬件加速
<activity
android:name=".WebViewActivity"
android:configChanges="orientation|screenSize"
android:hardwareAccelerated="true"
android:screenOrientation="portrait" />
- 处理全屏回调
CustomViewCallback mCallback;
View vCustom;
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
setFullscreen(true);
vCustom = view;
mCallback = callback;
if (vCustom != null) {
ViewGroup parent = (ViewGroup) vWeb.getParent();
parent.addView(vCustom);
}
}
@Override
public void onHideCustomView() {
setFullscreen(false);
if (vCustom != null) {
ViewGroup parent = (ViewGroup) vWeb.getParent();
parent.removeView(vCustom);
vCustom = null;
}
if (mCallback != null) {
mCallback.onCustomViewHidden();
mCallback = null;
}
}
- 设置全屏,切换屏幕方向
void setFullscreen(boolean fullscreen) {
if (fullscreen) {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
vToolbar.setVisibility(View.GONE);
vWeb.setVisibility(View.GONE);
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN, WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
vToolbar.setVisibility(View.VISIBLE);
vWeb.setVisibility(View.VISIBLE);
}
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
}
参考:
1、Android WebView 详解
2、WebView·开车指南
3、Android WebView 常用API参考手册