WebView 技术

WebView开发教程…持续整理中…

WebView 简介

WebView 提供了四个配置类来定制 WebView 的各种行为及表现,分别是:

  1. WebSettings
  2. WebViewClient
  3. WebChromeClient
  4. JavaScriptInterface

deprecated Api不再注明出来
所有没有写方法返回值的 Api,默认返回值为空类型 Void

WebView 介绍

继承关系

1
2
3
4
5
6
java.lang.Object
↳ android.view.View
↳ android.view.ViewGroup
↳ android.widget.AbsoluteLayout
↳ android.webkit.WebView

WebView 常用 Api 介绍

下面三个接口主要用于 WebView 与 JavaScript 交互,后面会介绍它的用法

  • addJavascriptInterface(Object object, String name)
  • removeJavascriptInterface(String name)
  • evaluateJavascript(String script, ValueCallback resultCallback)
  • loadUrl(String url)
  • loadUrl(String url, Map additionalHttpHeaders)
  • pageDown(boolean bottom)
  • pageUp(boolean top)
  • boolean canGoBack()
  • boolean canGoBackOrForward(int steps)
  • boolean canGoForward()
  • goBack()
  • goBackOrForward(int steps)
  • goForward()
  • boolean performLongClick()
  • reload()
  • stopLoading()
  • zoomBy(float zoomFactor)
  • boolean zoomIn()
  • boolean zoomOut()

  • clearCache(boolean includeDiskFiles)

  • clearClientCertPreferences(Runnable onCleared)
  • clearFormData()
  • clearHistory()
  • clearMatches()
  • clearSslPreferences()
  • computeScroll()
  • WebBackForwardList copyBackForwardList()
  • PrintDocumentAdapter createPrintDocumentAdapter(String documentName)
  • WebMessagePort[] createWebMessageChannel()
  • destroy()
  • boolean dispatchKeyEvent(KeyEvent event)
  • String findAddress(String addr)
  • int findAll(String find)
  • findAllAsync(String find)
  • View findFocus()
  • findNext(boolean forward)
  • flingScroll(int vx, int vy)
  • documentHasImages(Message response)
  • enableSlowWholeDocumentDraw()
  • AccessibilityNodeProvider getAccessibilityNodeProvider()
  • CharSequence getAccessibilityClassName()
  • SslCertificate getCertificate()
  • int getContentHeight()
  • Bitmap getFavicon()
  • Handler getHandler()
  • WebView.HitTestResult getHitTestResult()
  • String getOriginalUrl()
  • int getProgress()
  • WebSettings getSettings()
  • String getTitle()
  • String getUrl()

  • invokeZoomPicker()

  • boolean isPrivateBrowsingEnabled()
  • loadData(String data, String mimeType, String encoding)
  • loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)

  • InputConnection onCreateInputConnection(EditorInfo outAttrs)

  • boolean onDragEvent(DragEvent event)
  • onFinishTemporaryDetach()
  • onGenericMotionEvent(MotionEvent event)
  • onHoverEvent(MotionEvent event)
  • onKeyDown(int keyCode, KeyEvent event)
  • onKeyMultiple(int keyCode, int repeatCount, KeyEvent event)
  • onKeyUp(int keyCode, KeyEvent event)
  • onPause()
  • onProvideVirtualStructure(ViewStructure structure)
  • onResume()
  • onStartTemporaryDetach()
  • boolean onTouchEvent(MotionEvent event)
  • boolean onTrackballEvent(MotionEvent event)
  • onWindowFocusChanged(boolean hasWindowFocus)

  • pauseTimers()

  • postUrl(String url, byte[] postData)

  • postVisualStateCallback(long requestId, WebView.VisualStateCallback callback)
  • postWebMessage(WebMessage message, Uri targetOrigin)

  • boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate)

  • boolean requestFocus(int direction, Rect previouslyFocusedRect)
  • requestFocusNodeHref(Message hrefMsg)
  • requestImageRef(Message msg)
  • WebBackForwardList restoreState(Bundle inState)
  • resumeTimers()
  • WebBackForwardList saveState(Bundle outState)
  • saveWebArchive(String filename)
  • saveWebArchive(String basename, boolean autoname, ValueCallback callback)
  • setBackgroundColor(int color)
  • setDownloadListener(DownloadListener listener)
  • setFindListener(WebView.FindListener listener)
  • setInitialScale(int scaleInPercent)
  • setLayerType(int layerType, Paint paint)
  • setLayoutParams(ViewGroup.LayoutParams params)
  • setNetworkAvailable(boolean networkUp)
  • setOverScrollMode(int mode)
  • setScrollBarStyle(int style)
  • setWebChromeClient(WebChromeClient client)
  • setWebContentsDebuggingEnabled(boolean enabled)
  • setWebViewClient(WebViewClient client)
  • shouldDelayChildPressedState()

WebSettings

Manages settings state for a WebView. When a WebView is first created, it obtains a set of default settings. These default settings will be returned from any getter call. A WebSettings object obtained from WebView.getSettings() is tied to the life of the WebView. If a WebView has been destroyed, any method call on WebSettings will throw an IllegalStateException

WebSettings 是当 WebView 初次创建的时候默认创建的一个用来管理配置状态的类(一个配置集合,里面包含很多 set/get 方法),如果 WebView 销毁掉了,调用 WebSettings 里面的任何方法都是非法的。

获取 WebSettings

1
WebSettings webSetting = mWebView.getSettings();

WebSettings 常用 Api 介绍

WebSettings 配置类里面包含大量的 get/set 方法,所以下面 Api 中只介绍 set 方法

  • boolean enableSmoothTransition()
  • setAllowContentAccess(boolean allow)
  • setAllowFileAccess(boolean allow)
  • setAllowFileAccessFromFileURLs(boolean flag)
  • setAllowUniversalAccessFromFileURLs(boolean flag)
  • setAppCacheEnabled(boolean flag)
  • setAppCachePath(String appCachePath)
  • setBlockNetworkImage(boolean flag)
  • setBlockNetworkLoads(boolean flag)
  • setBuiltInZoomControls(boolean enabled)
  • setCacheMode(int mode)
  • setCursiveFontFamily(String font)
  • setDatabaseEnabled(boolean flag)
  • setDefaultFixedFontSize(int size)
  • setDefaultFontSize(int size)
  • setDefaultTextEncodingName(String encoding)
  • setDisabledActionModeMenuItems(int menuItems)
  • setDisplayZoomControls(boolean enabled)
  • setDomStorageEnabled(boolean flag)
  • setFantasyFontFamily(String font)
  • setFixedFontFamily(String font)
  • setGeolocationEnabled(boolean flag)
  • setJavaScriptCanOpenWindowsAutomatically(boolean flag)
  • setJavaScriptEnabled(boolean flag)
  • setLayoutAlgorithm(WebSettings.LayoutAlgorithm l)
  • setLoadWithOverviewMode(boolean overview)
  • setLoadsImagesAutomatically(boolean flag)
  • setMediaPlaybackRequiresUserGesture(boolean require)
  • setMinimumFontSize(int size)
  • setMinimumLogicalFontSize(int size)
  • setMixedContentMode(int mode)
  • setNeedInitialFocus(boolean flag)
  • setOffscreenPreRaster(boolean enabled)
  • setSansSerifFontFamily(String font)
  • setSaveFormData(boolean save)
  • setSerifFontFamily(String font)
  • setStandardFontFamily(String font)
  • setSupportMultipleWindows(boolean support)
  • setSupportZoom(boolean support)
  • setTextZoom(int textZoom)
  • setUseWideViewPort(boolean use)
  • setUserAgentString(String ua)
  • boolean supportMultipleWindows()
  • boolean supportZoom()

WebViewClient

WebView 代理,它主要是用来处理网页与浏览器交互时,处理各种通知和请求事件的。
实际使用过程中,开发者继承实现一个子类调用接口 setWebViewClient(WebViewClient client) 即可。

可重写方法说明

  • onPageStarted(WebView view, String url, Bitmap favicon)
    view
    url
    favicon
  • onPageFinished(WebView view, String url):
  • onPageCommitVisible(WebView view, String url):
  • boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
  • boolean shouldOverrideKeyEvent(WebView view, KeyEvent event)
  • WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request)
  • onUnhandledKeyEvent(WebView view, KeyEvent event)
  • onTooManyRedirects(WebView view, Message cancelMsg, Message continueMsg)
  • onScaleChanged(WebView view, float oldScale, float newScale)
  • onLoadResource(WebView view, String url)
  • onFormResubmission(WebView view, Message dontResend, Message resend)
  • doUpdateVisitedHistory(WebView view, String url, boolean isReload)
  • onReceivedClientCertRequest(WebView view, ClientCertRequest request)
  • onReceivedError(WebView view, WebResourceRequest request, WebResourceError error)
  • onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm)
  • onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse)
  • onReceivedLoginRequest(WebView view, String realm, String account, String args)
  • onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)

WebChromeClient

WebView chrome 代理,相对于 WebViewClient,它主要是用来处理 js,网站信息,加载进度等浏览器相关的事件。

可重写方法说明

  • Bitmap getDefaultVideoPoster():返回一个视频没有播放时候所显示的缩略图
  • View getVideoLoadingProgressView():返回一个视频加载过程中用于展示的 View
  • onHideCustomView():全屏取消的时候回调
  • onShowCustomView(View view, WebChromeClient.CustomViewCallback callback):视频全屏的时候回调

  • getVisitedHistory(ValueCallback callback)

  • onCloseWindow(WebView window)
  • boolean onConsoleMessage(ConsoleMessage consoleMessage)
  • boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg)
  • onGeolocationPermissionsHidePrompt()
  • onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback)
  • onJsAlert(WebView view, String url, String message, JsResult result)
  • onJsBeforeUnload(WebView view, String url, String message, JsResult result)
  • onJsConfirm(WebView view, String url, String message, JsResult result)
  • onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result)
  • onJsTimeout()
  • onPermissionRequest(PermissionRequest request)
  • onPermissionRequestCanceled(PermissionRequest request)
  • onProgressChanged(WebView view, int newProgress)
  • onReceivedIcon(WebView view, Bitmap icon)
  • onReceivedTitle(WebView view, String title)
  • onReceivedTouchIconUrl(WebView view, String url, boolean precomposed)
  • onRequestFocus(WebView view)
  • boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, WebChromeClient.FileChooserParams fileChooserParams)

Webview js 互调 - JavaScriptInterface

Annotation that allows exposing methods to JavaScript. Starting from API level JELLY_BEAN_MR1 and above, only methods explicitly marked with this annotation are available to the Javascript code. See addJavascriptInterface(Object, String) for more information about it.

该注解允许将指定的方法公开给 JavaScript 调用,要求 Api>=17,否则在 js 代码中是无法调用 android 端的方法的。
这个是结合 addJavascriptInterface 来使用。

WebView 调用 js 方法

WebView 调用 js 主要有两种方法:

  1. loadUrl - 无返回值

    1
    2
    String call = "javascript:CallJs()";
    webView.loadUrl(call);
  2. evaluateJavascript - 带有回调
    这个 api 只有在 KITKAT 以上才能使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @TargetApi(Build.VERSION_CODES.KITKAT)
    private void evaluateJavaScript(){
    mWebView.evaluateJavascript(" _CallJs()", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
    Log.d("returnStr:", s);
    }
    });
    }
  3. 带入参的 js 调用
    在 java 层调用 js 函数要入参时如果使用 json 结构体,一定要注意转义,否则会调用失败。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // 调用 js 方法名为 _CallJs 的函数,参数为 String 类型的 json串
    public void callJs(final String parasJson) {
    IMLogger.d("callJs parasJson : " + parasJson);
    if (mWebView == null) {
    IMLogger.d("webView instance is null");
    return;
    }
    runOnUiThread(new Runnable() {
    @Override
    public void run() {
    // 拼装 javascript 格式的方法调用
    StringBuilder js = new StringBuilder(" _CallJs");
    js.append("('").append(parasJson).append("')");
    // 异步调用,避免阻塞
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    mWebView.evaluateJavascript(js.toString(), new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
    notifyWebViewJS(s, CODE_WEBVIEW_CALLJS_RETURN);
    }
    });
    } else {
    mWebView.loadUrl("javascript:" + js.toString());
    }
    }
    });
    }

    在拼装 parasJson 的时候要注意最终的效果是类似这种 “ _CallJs(‘parasJson’)”,这里注意单引号的使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    String text = "{\"key3\":\"value3\",\"key2\":\"value2\",\"key1\":\"value1\"}";
    JSONObject jsonObject = new JSONObject();
    try {
    jsonObject.put("key1", "value1");
    jsonObject.put("key2", "value2");
    jsonObject.put("key3", "value3");
    } catch (JSONException e) {
    e.printStackTrace();
    }
    callJs(jsonObject.toString());

    被调用的 js 方法如下:

    1
    2
    3
    4
    5
    6
    7
    8
    // JS提供给Java调用的方法
    function _CallJs(arg) {
    // 获取 id 为 hello 的对象(这里是指整个页面);再获取它的 html代码
    document.getElementById("hello").innerHTML += ("<br/>" + arg);
    }
    function _CallJs() {
    }

js 调用 WebView 方法

  1. 申明可以被 js 调用的方法
    使用注解JavascriptInterface

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // JS调用 如果 targetSdkVersion >=17,一定要加注解,否则JS无法调用这个方法
    @JavascriptInterface
    public void jsCallNative(String jsParasJson) {
    IMLogger.d("jsCallNative jsParasJson=" + jsParasJson);
    sendMessageToManager(WebViewManager.MSG_JS_CALL, jsParasJson);
    }
    // 返回值调用
    public String jsCallNative() {
    sendMessageToManager(WebViewManager.MSG_JS_CALL, jsParasJson);
    return "java return value";
    }
  2. 注册 JavaScriptInterface
    该函数的声明及调用如下:

    1
    2
    3
    4
    5
    // 声明
    // void addJavascriptInterface (Object object, String name)
    // 使用
    // 第一个参数是一个`Object`,它与第二个参数存在类成员的关系,比如上面的`jsCallNative`方法,是写在`WebViewActivity`中的
    mWebView.addJavascriptInterface(WebViewActivity.this, "AndroidObj");

    这个接口的作用是把 this 所代表的类映射为 js 中的 android 对象,它的引用名字是 AndroidObj,js 侧在调用 java 方法的时候,要确保本地这个对象已经创建,否则会调用失败。

  3. js 调用
    在 js 中的调用逻辑如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // js调用android方法
    function jsCallJavaByInterface(arg) {
    AndroidObj.jsCallNative(arg);
    }
    // js调用android方法 带返回值
    function jsCallJavaByInterface() {
    var str = AndroidObj.jsCallNative();
    console.log(str);
    }

视频支持

In order to support inline HTML5 video in your application you need to have hardware acceleration turned on.

视频播放需要打开硬件加速,经常有人在集成 WebView 的时候碰到播放视频的时候,有声音没有图像,一般是这个原因导致。

全屏播放

In order to support full screen — for video or other HTML content — you need to set a WebChromeClient and implement both onShowCustomView(View, WebChromeClient.CustomViewCallback) and onHideCustomView(). If the implementation of either of these two methods is missing then the web contents will not be allowed to enter full screen. Optionally you can implement getVideoLoadingProgressView() to customize the View displayed whilst a video is loading.

WebView 对全屏播放的支持需要重写 WebChromeClient 类,如果不重写,WebView 在播放视频的时候,不会有全屏的按钮。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public Bitmap getDefaultVideoPoster() {
if(getActivity() == null) {
return null;
}
return BitmapFactory.decodeResource(getActivity().getApplicationContext().getResources(),
R.drawable.video_poster);
}
@Override
public void onShowCustomView(View view,
WebChromeClient.CustomViewCallback callback) {
// if a view already exists then immediately terminate the new one
if (mCustomView != null) {
onHideCustomView();
return;
}
// 1. Stash the current state
mCustomView = view;
mOriginalSystemUiVisibility = getActivity().getWindow().getDecorView().getSystemUiVisibility();
mOriginalOrientation = getActivity().getRequestedOrientation();
// 2. Stash the custom view callback
mCustomViewCallback = callback;
// 3. Add the custom view to the view hierarchy
FrameLayout decor = (FrameLayout) getActivity().getWindow().getDecorView();
decor.addView(mCustomView, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
// 4. Change the state of the window
getActivity().getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_IMMERSIVE);
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
@Override
public void onHideCustomView() {
// 1. Remove the custom view
FrameLayout decor = (FrameLayout) getActivity().getWindow().getDecorView();
decor.removeView(mCustomView);
mCustomView = null;
// 2. Restore the state to it's original form
getActivity().getWindow().getDecorView()
.setSystemUiVisibility(mOriginalSystemUiVisibility);
getActivity().setRequestedOrientation(mOriginalOrientation);
// 3. Call the custom view callback
mCustomViewCallback.onCustomViewHidden();
mCustomViewCallback = null;
}
});

Webview 无法显示图片问题

当发现无法显示图片时,首先要考虑下面两个配置

1
2
3
4
// 启用 js
webSetting.setJavaScriptEnabled(true);
// 关闭 image 显示阻塞
webSetting.setBlockNetworkImage(false);

setBlockNetworkImage 默认值为 false;该方法一般用来解决网页加载缓慢的问题,网页开始加载的时候设为 true,在网页加载完毕后,再设为 false;

如果设置了上面两个还是无法显示图片(http 类型),就需要考虑 https 站点加载 http 资源问题,这种问题多出现在 5.0 及以上机型中。
此处涉及接口 setMixedContentMode, 简单理解就是是否允许 webview 在加载 https 站点时能否使用 http 资源,5.0 以上 Android webview 禁止在加载 https 站点时加载 http 图片资源,这样 就导致图片无法显示;

Configures the WebView’s behavior when a secure origin attempts to load a resource from an insecure origin. By default, apps that target KITKAT or below default to MIXED_CONTENT_ALWAYS_ALLOW. Apps targeting LOLLIPOP default to MIXED_CONTENT_NEVER_ALLOW. The preferred and most secure mode of operation for the WebView is MIXED_CONTENT_NEVER_ALLOW and use of MIXED_CONTENT_ALWAYS_ALLOW is strongly discouraged.

1
2
3
4
5
6
7
// 接口
void setMixedContentMode (int mode);
// 取值
MIXED_CONTENT_ALWAYS_ALLOW: // 允许使用 http 资源 (4.4 及以下机型默认配置)
MIXED_CONTENT_COMPATIBILITY_MODE: // 兼容模式(允许部分使用 http 资源,系统会帮忙阻挡一部分非常的 http 资源)
MIXED_CONTENT_NEVER_ALLOW: //不允许(5.0 及以上机型默认配置)

Webview 私有协议支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mWebView.setWebViewClient(new WebViewClient() {
@SuppressLint("NewApi")
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
IMLogger.d("loading url:" + url);
if (url.startsWith(" _Test:")) {
// 私有协议处理。。。
} else if (url != null && url.startsWith("mailto:") // 新增邮箱、地图、电话、短信协议跳转
|| url.startsWith("geo:") || url.startsWith("tel:") || url.startsWith("smsto:")) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
} else {
view.loadUrl(url);
}
return true;
}
}

Webview 软件盘遮挡问题

TBS X5 内核

由 QQ浏览器 提供的第三方 WebView 组件,使用比较简单,而且可以在 X5 内核跟系统内核之间自由切换,具体见下面的
接入指南

问题:目前对 64位 机器支持不是很好

引用