WebResourceRequest
添加于API21,封装了一个Web资源的请求信息,包含:请求地址,请求方法,请求头,是否主框架,是否用户点击,是否重定向
WebResourceResponse
封装了一个Web资源的响应信息,包含:响应数据流,编码,MIME类型,API21后添加了响应头,状态码与状态描述
WebResourceError
添加于API23,封装了一个Web资源的错误信息,包含错误码和描述
CookieManager
管理用于WebView的cookies。。
WebViewDatabase
存储与管理以下几类浏览数据:
表单自动填充的的用户名与密码
HTTP认证的用户名与密码
曾经输入过的文本(比如自动完成)
WebStorage
用于管理WebView提供的JS存储API,比如Application Cache API,Web SQL Database API,HTML5 Web Storage API
GeolocationPermissions
用于管理WebView的JS Geolocation API
HttpAuthHandler
表示一个HTTP认证请求,提供了方法操作(proceed/cancel)请求
SslErrorHandler
表示一个处理SSL错误的请求,提供了方法操作(proceed/cancel)请求
ClientCertRequest
表示一个证书请求,提供了方法操作(proceed/cancel/ignore)请求
JsResult
用于处理底层JS发起的请求,为客户端提供一些方法指明应进行的操作,比如确认或取消。
WebView
public String
getUrl
();
public String
getOriginalUrl
();
public String
getTitle
();
public Bitmap
getFavicon
();
public
int
getProgress
();
public
void
setNetworkAvailable
(
boolean networkUp)
public
void
setInitialScale
(
int scaleInPercent);
public
void
loadUrl
(String url);
public
void
loadUrl
(String url, Map<String, String> additionalHttpHeaders);
public
void
postUrl
(String url,
byte[] postData);
public
void
reload
();
public
void
loadData
(String data, String mimeType, String encoding);
public
void
loadDataWithBaseURL
(String baseUrl, String data, String mimeType, String encoding, String historyUrl);
public
void
addJavascriptInterface
(Object object, String name);
public
void
removeJavascriptInterface
(String name);
public
void
evaluateJavascript
(String script, ValueCallback<String> resultCallback)
public
boolean
canGoForward
();
public
boolean
canGoBackOrForward
(
int steps);
public
void
goBack
();
public
void
goForward
();
public
void
goBackOrForward
(
int steps);
public
void
clearHistory
();
public
void
setFindListener
(FindListener listener);
public
void
findAllAsync
(String find);
public
void
findNext
(
boolean forward);
public
void
clearMatches
();
public
void
saveWebArchive
(String filename);
public
void
saveWebArchive
(String basename,
boolean autoname, ValueCallback<String> callback);
public
void
pageUp
(
boolean top);
public
void
pageDown
(
boolean bottom);
public
void
zoomBy
(
float factor);
public
boolean
zoomIn
();
public
boolean
zoomOut
();
public
void
clearCache
(
boolean includeDiskFiles);
public
void
clearFormData
();
public
void
clearSslPreferences
();
public
void
documentHasImages
(Message msg);
public
void
requestFocusNodeHref
(Message msg);
public
void
requestImageRef
(Message msg)
public
static
void
clearClientCertPreferences
(Runnable onCleared)
public
static
void
setWebContentsDebuggingEnabled
(
boolean enabled)
settings.setDomStorageEnabled(
true);
settings.setDatabaseEnabled(
true);
settings.setAppCacheEnabled(
true);
settings.setAppCachePath(context.getCacheDir().getAbsolutePath());
settings.setGeolocationEnabled(
true);
settings.setSaveFormData(
true);
settings.setNeedInitialFocus(
true);
settings.setUseWideViewPort(
true);
settings.setLoadWithOverviewMode(
true);
settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
settings.setJavaScriptEnabled(
true);
settings.setSupportMultipleWindows(
false);
settings.setJavaScriptCanOpenWindowsAutomatically(
false);
settings.setAllowContentAccess(
true);
settings.setAllowFileAccess(
true);
settings.setAllowFileAccessFromFileURLs(
false);
settings.setAllowUniversalAccessFromFileURLs(
false);
settings.setLoadsImagesAutomatically(
true);
settings.setBlockNetworkImage(
false);
settings.setBlockNetworkLoads(
false);
settings.setSupportZoom(
true);
settings.setBuiltInZoomControls(
false);
settings.setDisplayZoomControls(
true);
settings.setDefaultTextEncodingName(
"UTF-8");
settings.setDefaultFontSize(
16);
settings.setDefaultFixedFontSize(
16);
settings.setMinimumFontSize(
8);
settings.setMinimumLogicalFontSize(
8);
settings.setTextZoom(
100);
settings.setStandardFontFamily(
"sans-serif");
settings.setSerifFontFamily(
"serif");
settings.setSansSerifFontFamily(
"sans-serif");
settings.setFixedFontFamily(
"monospace");
settings.setCursiveFontFamily(
"cursive");
settings.setFantasyFontFamily(
"fantasy");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
settings.setMediaPlaybackRequiresUserGesture(
true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
settings.setOffscreenPreRaster(
false);
if (isNetworkConnected(context)) {
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
}
else {
settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
settings.setRenderPriority(WebSettings.RenderPriority.HIGH);
settings.setDatabasePath(context.getDir(
"database", Context.MODE_PRIVATE).getPath());
settings.setGeolocationDatabasePath(context.getFilesDir().getPath());
WebSettings settings = web.getSettings();
settings.setAppCacheEnabled(
true);
settings.setAppCachePath(context.getCacheDir().getAbsolutePath());
settings.setDomStorageEnabled(
true);
settings.setDatabaseEnabled(
true);
settings.setUseWideViewPort(
true);
settings.setLoadWithOverviewMode(
true);
settings.setJavaScriptEnabled(
true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
if (isNetworkConnected(context)) {
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
}
else {
settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
public
boolean
shouldOverrideUrlLoading
(WebView view, String url) {
return
false;
@TargetApi(Build.VERSION_CODES.N)
public
boolean
shouldOverrideUrlLoading
(WebView view, WebResourceRequest request) {
return shouldOverrideUrlLoading(view, request.getUrl().toString());
public WebResourceResponse
shouldInterceptRequest
(WebView view, String url) {
return
null;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public WebResourceResponse
shouldInterceptRequest
(WebView view, WebResourceRequest request) {
return shouldInterceptRequest(view, request.getUrl().toString());
public
void
onPageStarted
(WebView view, String url, Bitmap favicon) {
public
void
onPageFinished
(WebView view, String url) {
public
void
onLoadResource
(WebView view, String url) {
public
void
onPageCommitVisible
(WebView view, String url) {
public
void
onReceivedError
(WebView view,
int errorCode, String description, String failingUrl) {
@TargetApi(Build.VERSION_CODES.M)
public
void
onReceivedError
(WebView view, WebResourceRequest request, WebResourceError error) {
if (request.isForMainFrame()) {
onReceivedError(view, error.getErrorCode(), error.getDescription().toString(), request.getUrl().toString());
public
void
onReceivedHttpError
(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
public
void
onFormResubmission
(WebView view, Message dontResend, Message resend) {
dontResend.sendToTarget();
public
void
doUpdateVisitedHistory
(WebView view, String url,
boolean isReload) {
public
void
onReceivedSslError
(WebView view, SslErrorHandler handler, SslError error) {
handler.cancel();
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public
void
onReceivedClientCertRequest
(WebView view, ClientCertRequest request) {
request.cancel();
public
void
onReceivedHttpAuthRequest
(WebView view, HttpAuthHandler handler, String host, String realm) {
handler.cancel();
public
void
onReceivedLoginRequest
(WebView view, String realm, String account, String args) {
public
boolean
shouldOverrideKeyEvent
(WebView view, KeyEvent event) {
return
false;
public
void
onUnhandledKeyEvent
(WebView view, KeyEvent event) {
super.onUnhandledKeyEvent(view, event);
public
void
onScaleChanged
(WebView view,
float oldScale,
float newScale) {
public
void
getVisitedHistory
(ValueCallback<String[]> callback) {
public Bitmap
getDefaultVideoPoster
() {
return
null;
public View
getVideoLoadingProgressView
() {
return
null;
public
void
onProgressChanged
(WebView view,
int newProgress) {
public
void
onReceivedTitle
(WebView view, String title) {
public
void
onReceivedIcon
(WebView view, Bitmap icon) {
public
void
onReceivedTouchIconUrl
(WebView view, String url,
boolean precomposed) {
public
void
onShowCustomView
(View view, CustomViewCallback callback) {
public
void
onHideCustomView
() {
public
boolean
onJsAlert
(WebView view, String url, String message, JsResult result) {
return
false;
public
boolean
onJsConfirm
(WebView view, String url, String message, JsResult result) {
return
false;
public
boolean
onJsPrompt
(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
return
false;
public
boolean
onJsBeforeUnload
(WebView view, String url, String message, JsResult result) {
return
false;
public
void
onGeolocationPermissionsShowPrompt
(String origin, GeolocationPermissions.Callback callback) {
public
void
onGeolocationPermissionsHidePrompt
() {
public
boolean
onCreateWindow
(WebView view,
boolean isDialog,
boolean isUserGesture, Message resultMsg) {
return
false;
public
void
onCloseWindow
(WebView window) {
public
void
onRequestFocus
(WebView view) {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public
void
onPermissionRequest
(PermissionRequest request) {
request.deny();
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public
void
onPermissionRequestCanceled
(PermissionRequest request) {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public
boolean
onShowFileChooser
(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
return
false;
public
boolean
onConsoleMessage
(ConsoleMessage consoleMessage) {
return
false;
onPageStarted() -> onPageFinished()
onPageStarted() -> redirection -> ... -> onPageFinished()
shouldOverrideUrlLoading() -> ...
shouldOverrideUrlLoading() -> onPageStarted() -> onPageFinished()
shouldOverrideUrlLoading() -> onPageStarted() -> redirection -> ... -> onPageFinished()
如果页面B中直接输出
window.location="http://example.com"
,那页面B不会被加入回退栈,回退将直接回到A页
如果页面B加载完成后,比如用
setTimeout
延迟了,那页面B会被加入回退栈,当回退到页面A时会再执行跳转,这会导致回退功能看起来不正常,需要快速回退两次才能回到A页面
视口(viewport)
https://developer.android.com/guide/webapps/targeting.html
https://developer.mozilla.org/en-US/docs/Mozilla/Mobile/Viewport_meta_tag
https://developer.mozilla.org/zh-CN/docs/Web/CSS/@viewport
视口是一个为网页提供绘图区域的矩形。
你可以指定数个视口属性,比如尺寸和初始缩放系数(initial scale)。其中最重要的是
视口宽度
,它定义了网页水平方向的可用像素总数(可用的CSS像素数)。
多数 Android 上的网页浏览器(包括 Chrome)设置默认视口为一个大尺寸(被称为”
wide viewport mode”
,宽约 980px)。
也有许多浏览器默认会尽可能缩小以显示完整的视口宽度(被称为”
overview mode
“)。
settings.setUseWideViewPort(
true);
settings.setLoadWithOverviewMode(
true);
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"]
<
title>Example
</
title>
<
meta
name=
"viewport"
content=
"width=device-width, user-scalable=no" />
</
head>
Cookie 是服务器发送到用户浏览器并保存在浏览器上的一块数据,它会在浏览器下一次发起请求时被携带并发送到服务器上。
可通过Cookie保存浏览信息来获得更轻松的在线体验,比如保持登录状态、记住偏好设置,并提供本地的相关内容。
会话Cookie 与 持久Cookie
会话cookie不需要指定Expires和Max-Age,浏览器关闭之后它会被自动删除。
持久cookie指定了Expires或Max-Age,会被存储到磁盘上,不会因浏览器而失效。
第一方Cookie 与 第三方Cookie
每个Cookie都有与之关联的域,与页面域一样的就是第一方Cookie,不一样的就是第三方Cookie。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().setAcceptThirdPartyCookies(vWeb,
true);
CookieManager.getInstance().getCookie(url);
CookieManager.getInstance().setCookie(url, value);
CookieManager.getInstance().setCookie(url, cookieName +
"=");
public
static
void
remove
(String url) {
CookieManager cm = CookieManager.getInstance();
for (String cookie : cm.getCookie(url).split(
"; ")) {
cm.setCookie(url, cookie.split(
"=")[
0] +
"=");
flush();
public
static
void
remove
(
boolean sessionOnly) {
CookieManager cm = CookieManager.getInstance();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (sessionOnly) {
cm.removeSessionCookies(
null);
}
else {
cm.removeAllCookies(
null);
}
else {
if (sessionOnly) {
cm.removeSessionCookie();
}
else {
cm.removeAllCookie();
flush();
public
static
void
flush
() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().flush();
}
else {
CookieSyncManager.getInstance().sync();
public
class
WebkitCookieHandler
extends
CookieHandler {
private
static
final String TAG = WebkitCookieHandler.class.getSimpleName();
private CookieManager wcm;
public
WebkitCookieHandler
() {
this.wcm = CookieManager.getInstance();
@Override
public
void
put
(URI uri, Map<String, List<String>> headers)
throws IOException {
if ((uri ==
null) || (headers ==
null)) {
return;
String url = uri.toString();
for (String headerKey : headers.keySet()) {
if ((headerKey ==
null) || !(headerKey.equalsIgnoreCase(
"set-cookie2") || headerKey.equalsIgnoreCase(
"set-cookie"))) {
continue;
for (String headerValue : headers.get(headerKey)) {
Log.e(TAG, headerKey +
": " + headerValue);
this.wcm.setCookie(url, headerValue);
@Override
public Map<String, List<String>> get(URI uri, Map<String, List<String>> headers)
throws IOException {
if ((uri ==
null) || (headers ==
null)) {
throw
new IllegalArgumentException(
"Argument is null");
String url = uri.toString();
String cookie =
this.wcm.getCookie(url);
Log.e(TAG,
"cookie: " + cookie);
if (cookie !=
null) {
return Collections.singletonMap(
"Cookie", Arrays.asList(cookie));
}
else {
return Collections.emptyMap();
WebSettings.LOAD_DEFAULT
根据cache-control决定是否从网络上取数据
WebSettings.LOAD_CACHE_ELSE_NETWORK
无网,离线加载,优先加载缓存(即使已经过期)
WebSettings.LOAD_NO_CACHE
仅从网络加载
WebSettings.LOAD_CACHE_ONLY
仅从缓存加载
if (isNetworkConnected(mActivity)) {
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
}
else {
settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
预加载(Preload)
一个简单的预加载示例(shouldInterceptRequest)
点击
assets/demo.xml
里的链接”hello”时会加载本地的
assets/hello.html
assets/demo.xml
@Override
public WebResourceResponse
shouldInterceptRequest
(WebView view, String url) {
return preload(
"assets/", url);
WebResourceResponse
preload
(String path, String url) {
if (!url.contains(path)) {
return
null;
String local = url.replaceFirst(
"^http.*" + path,
"");
try {
InputStream is = getApplicationContext().getAssets().open(local);
String ext = MimeTypeMap.getFileExtensionFromUrl(local);
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext);
return
new WebResourceResponse(mimeType,
"UTF-8", is);
}
catch (Exception e) {
e.printStackTrace();
return
null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
vWeb.evaluateJavascript(
"111+222",
new ValueCallback<String>() {
@Override
public
void
onReceiveValue
(String value) {
地理位置(Geolocation)
https://developer.mozilla.org/zh-CN/docs/Web/API/Geolocation/Using_geolocation
需要以下权限
<
uses-permission
android:name=
"android.permission.INTERNET"/>
<
uses-permission
android:name=
"android.permission.ACCESS_FINE_LOCATION"/>
<
uses-permission
android:name=
"android.permission.ACCESS_COARSE_LOCATION"/>
@Override
public
void
onGeolocationPermissionsShowPrompt
(String origin, GeolocationPermissions.Callback callback) {
boolean allow =
true;
boolean retain =
false;
callback.invoke(origin,
true,
false);
@Override
public
void
onGeolocationPermissionsHidePrompt
() {
注:从API24开始,仅支持安全源(https)的请求,非安全源的请求将自动拒绝且不调用 onGeolocationPermissionsShowPrompt 与 onGeolocationPermissionsHidePrompt
弹框(alert/confirm/prompt/onbeforeunload)
在javascript中使用 alert/confirm/prompt 会弹出对话框,可通过重载 WebChromeClient 的下列方法控制弹框的交互,比如替换系统默认的对话框或屏蔽这些对话框
@Override
public
boolean
onJsAlert
(WebView view, String url, String message, JsResult result) {
return
false;
@Override
public
boolean
onJsConfirm
(WebView view, String url, String message, JsResult result) {
return
false;
@Override
public
boolean
onJsPrompt
(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
return
false;
@Override
public
boolean
onJsBeforeUnload
(WebView view, String url, String message, JsResult result) {
return
false;
当H5请求全屏时,会回调
WebChromeClient.onShowCustomView
方法
当H5退出全屏时,会回调
WebChromeClient.onHideCustomView
方法
1.manifest
自己处理屏幕尺寸方向的变化(切换屏幕方向时不重建activity)
WebView播放视频需要开启硬件加速
android:name=
".WebViewActivity"
android:configChanges=
"orientation|screenSize"
android:hardwareAccelerated=
"true"
android:screenOrientation=
"portrait" />
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:orientation=
"vertical">
<
android.support.v7.widget.Toolbar
android:id=
"@+id/toolbar"
style=
"@style/Toolbar.Back"/>
<
FrameLayout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent">
<
WebView
android:id=
"@+id/web"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"/>
</
FrameLayout>
</
LinearLayout>
@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);
vWeb.setWebViewClient(
null);
vWeb.setWebChromeClient(
null);
vWeb.loadDataWithBaseURL(
null,
"",
"text/html",
"utf-8",
null);
vWeb.clearHistory();
((ViewGroup) vWeb.getParent()).removeView(vWeb);
vWeb.destroy();
vWeb =
null;
https://developer.android.com/reference/android/webkit/package-summary.html
Fullscreen API 全屏显示网页
http://calefy.org/2012/06/03/fullscreen-web-page-width-fullscreen-api.html
WebView实现全屏播放的一种方法
https://segmentfault.com/a/1190000007561455
第一方Cookie和第三方Cookie区别
https://www.biaodianfu.com/first-party-cookie-and-third-party-cookie.html
Android WebView的Js对象注入漏洞解决方案
http://blog.csdn.net/leehong2005/article/details/11808557
Android安全开发之WebView中的地雷
http://yaq.qq.com/blog/10
Android WebView:性能优化不得不说的事
https://juejin.im/entry/57d6434067f3560057e50b20