Android WebView Development (1): Basic Application

1. Android WebView development (1): basic application
2. Android WebView development (2): WebView and Native interaction
3. Android WebView development (3): WebView performance optimization
4. Android WebView development (4): WebView independent process solution
Five, Android WebView development (five): custom WebView toolbar

Attached GitHub source code: WebViewExplore


1. Basic configuration of WebView

WebSettings ws = getSettings();
ws.setBuiltInZoomControls(true);// 隐藏缩放按钮
ws.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);// 排版适应屏幕

ws.setUseWideViewPort(true);// 可任意比例缩放
ws.setLoadWithOverviewMode(true);// setUseWideViewPort方法设置webview推荐使用的窗口。setLoadWithOverviewMode方法是设置webview加载的页面的模式。

ws.setSaveFormData(true);// 保存表单数据
ws.setJavaScriptEnabled(true); // 是否能与JS交互【如果业务中无JS交互,建议将此项关闭】
ws.setGeolocationEnabled(true);// 启用地理定位【如果业务中无此业务,建议将此项关闭】
ws.setDomStorageEnabled(true);
ws.setJavaScriptCanOpenWindowsAutomatically(true);//允许JS Alert对话框等打开【如果业务中无此业务,建议将此项关闭】
ws.setSupportMultipleWindows(true);// 新加

2. WebView supports playing music

//是否支持播放音乐
ws.setPluginState(WebSettings.PluginState.ON);
ws.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);

//是否需要用户点击才播放
ws.setMediaPlaybackRequiresUserGesture(true);

3. WebView supports video playback

Android WebView plays video (including full screen playback)

4. WebChromeClient

/**
 * WebChromeClient是辅助WebView处理Javascript的对话框,网站图标,网站title,加载进度等
*/
setWebChromeClient(new XWebChromeClient());

The specific coverage method is as follows: 

    public static class XWebChromeClient extends WebChromeClient {

        /**
         * 获取网页加载进度
         * @param view
         * @param newProgress
         */
        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            super.onProgressChanged(view, newProgress);
            Log.d(TAG, "onProgressChanged---> newProgress:" + newProgress);
        }

        /**
         * 获取网站标题 (Android 6.0 以下通过title获取【捕捉HTTP ERROR】)
         *
         * @param view
         * @param title
         */
        @Override
        public void onReceivedTitle(WebView view, String title) {
            super.onReceivedTitle(view, title);
            Log.d(TAG, "onReceivedTitle---> title:" + title);
            if (webTitleCallBack != null) {
                webTitleCallBack.onReceived(title);
            }
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                if (title.contains("404") || title.contains("500") || title.contains("Error")) {
                    view.loadUrl("about:blank"); // 避免出现默认的错误界面
                    // 在这里可以考虑显示自定义错误页
                    // showErrorPage();
                }
            }
        }

        /**
         * 网站图标
         *
         * @param view
         * @param icon
         */
        @Override
        public void onReceivedIcon(WebView view, Bitmap icon) {
            super.onReceivedIcon(view, icon);
            Log.d(TAG, "icon:" + icon);
        }

        /**
         * 拦截Alert弹框
         *
         * @param view
         * @param url
         * @param message
         * @param result
         * @return
         */
        @Override
        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
            Log.d(TAG, "onJsAlert");
            return super.onJsAlert(view, url, message, result);
        }

        /**
         * 拦截 confirm弹框
         *
         * @param view
         * @param url
         * @param message
         * @param result
         * @return
         */
        @Override
        public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
            Log.d(TAG, "onJsConfirm");
            return super.onJsConfirm(view, url, message, result);
        }

        /**
         * 打印console信息
         *
         * @param consoleMessage
         * @return
         */
        @Override
        public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
            Log.d(TAG, "onConsoleMessage");
            return super.onConsoleMessage(consoleMessage);
        }

        /**
         * 该方法在web页面请求某个尚未被允许或拒绝的权限时回调
         *
         * @param request
         */
        @Override
        public void onPermissionRequest(PermissionRequest request) {
            super.onPermissionRequest(request);
            Log.d(TAG, "onPermissionRequest---> request:" + request);
        }
    }

5. WebViewClient

/**
 * WebViewClient就是帮助WebView处理各种通知、请求事件的
 */
setWebViewClient(new XWebViewClient());

The specific coverage method is as follows: 

    public class XWebViewClient extends WebViewClient {

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
            Log.d(TAG, "onPageStarted---> url:" + url);
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            Log.d(TAG, "onPageFinished---> url:" + url);
        }

        /**
         * WEB页面加载错误时回调,这些错误通常都是由无法与服务器正常连接引起的。
         *
         * @param view
         * @param errorCode
         * @param description
         * @param failingUrl
         */
        //Android6.0之前的方法 【在新版本中也可能被调用,所以加上一个判断,防止重复显示】
        @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            super.onReceivedError(view, errorCode, description, failingUrl);
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                // 断网或者网络连接超时
                showReceivedErrorPage(view, errorCode, description, failingUrl);
            }
        }

        /**
         * 当服务器返回错误码时回调
         *
         * @param view
         * @param request
         * @param errorResponse
         */
        //6.0新增方法
        @RequiresApi(api = Build.VERSION_CODES.M)
        @Override
        public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
            super.onReceivedHttpError(view, request, errorResponse);
            // 这个方法在6.0才出现
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                int statusCode = 0;
                if (errorResponse != null) {
                    statusCode = errorResponse.getStatusCode();
                }
                Log.d(TAG, "onReceivedHttpError---> code = " + statusCode);
                if (404 == statusCode || 500 == statusCode) {
                    view.loadUrl("about:blank");// 避免出现默认的错误界面
                    // 在这里可以考虑显示自定义错误页
                    // showErrorPage();
                }
            }
        }
    }

There are also the following methods, especially when using them:

1. Redirection problem:

The shouldOverrideUrlLoading method can be used to judge and process redirection:

        /**
         * 重定向分析:
         *
         * @param view
         * @param request
         * @return true: 表示当前url已经加载完成,即使url还会重定向都不会再进行加载
         * false: 表示此url默认由系统处理,该重定向还是重定向,直到加载完成
         */
        //Android7.0之后的方法
        @RequiresApi(api = Build.VERSION_CODES.N)
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
            Log.d(TAG, "shouldOverrideUrlLoading new---> url:" + request.getUrl());

            analysisRequest(request);

            String url = (request.getUrl()).toString();
            boolean hasGesture = request.hasGesture();
            boolean isRedirect = request.isRedirect();

            return shouldOverride(view, url);
        }

 The cases that need to be considered for WebView redirection are as follows:

  • 1. It is the most common http url [excluding .doc, .apk and other download urls]
  • 2. The downloaded http url [such as .doc .apk, etc.]
  • 3. Non-http or https custom url [such as "weixin:// alipays://, etc.]
  • [deprecated] If you want the app not to be automatically activated when you open the web page, you can judge by clicking request.hasGesture() [whether or not]. If it is true, the third-party app will be activated. (This scheme is sometimes not very accurate, so the following scheme can be used)
  • [recommend] Define a boolean value such as: isClickWeb = false, and assign it to true in the onTouchEvent DOWN method. Just add judgment at the necessary position [For details, please refer to the code]
        /**
         * 自定义重定向处理方法
         * @param view
         * @param url
         * @return
         */
        private boolean shouldOverride(WebView view, final String url) {
            //业务需要可做处理
            redirectionJudge(view, url);

            if (SchemeUtil.isHttpProtocol(url) && !SchemeUtil.isDownloadFile(url)) {
                return false;
            }

            if (SchemeUtil.isHttpProtocol(url) && SchemeUtil.isDownloadFile(url)) {
                if (isClickWeb) {
                    openDialog(url);
                    return true;
                }
            }

            if (!SchemeUtil.isHttpProtocol(url)) {
                boolean isValid = SchemeUtil.isSchemeValid(context, url);
                if (isValid && isClickWeb) {
                    openDialog(url);
                } else {
                    Log.d(TAG, "此scheme无效[比如手机中未安装该app]");
                }
                return true;
            }
            return false;
        }

2. Realize preloading:

Resource preloading can be implemented in the shouldInterceptRequest method  :

        /**
         * 【实现预加载】
         * 有时候一个页面资源比较多,图片,CSS,js比较多,还引用了JQuery这种庞然巨兽,
         * 从加载到页面渲染完成需要比较长的时间,有一个解决方案是将这些资源打包进APK里面,
         * 然后当页面加载这些资源的时候让它从本地获取,这样可以提升加载速度也能减少服务器压力。
         */
        @Nullable
        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
            if (request == null) {
                return null;
            }
            String url = request.getUrl().toString();
            Log.d(TAG, "shouldInterceptRequest---> " + url);
            return getWebResourceResponse(url);
        }
        protected WebResourceResponse getWebResourceResponse(String url) {
            //此处[tag]等需要跟服务端协商好,再处理
            if (url.contains("[tag]")) {
                try {
                    String localPath = url.replaceFirst("^http.*[tag]\\]", "");
                    InputStream is = getContext().getAssets().open(localPath);
                    Log.d(TAG, "shouldInterceptRequest: localPath " + localPath);
                    String mimeType = "text/javascript";
                    if (localPath.endsWith("css")) {
                        mimeType = "text/css";
                    }
                    return new WebResourceResponse(mimeType, "UTF-8", is);
                } catch (IOException e) {
                    e.printStackTrace();
                    return null;
                }
            } else {
                return null;
            }
        }

3. Increase the error page display limit:

In the onReceivedError method, use  request.isForMainFrame() || url.equals(getUrl() to reduce the display of error pages as little as possible. That is, the error page is only displayed when the error page is the main page, avoiding the entire page such as An icon, etc. is displayed incorrectly, which affects the entire page [such as some URLs of NetEase Music, this situation has occurred, and this method can avoid the display of error pages].

        /**
         * 此方法中加载错误页面的时候,需要判断下 isForMainFrame 是否为true 亦或者 当前url跟加载的url是否为同一个url。
         *
         * @param view
         * @param request
         * @param error
         */
        //Android6.0之后的方法
        @RequiresApi(api = Build.VERSION_CODES.M)
        @Override
        public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
            super.onReceivedError(view, request, error);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                String url = request.getUrl().toString();
                int errorCode = error.getErrorCode();
                String description = error.getDescription().toString();
                Log.d(TAG, "onReceivedError---> " + " url:" + url + "errorCode:" + errorCode + " description:" + description + " failingUrl:" + url + " request.isForMainFrame():" + request.isForMainFrame());
                // 如果当前网络请求是为main frame创建的,则显示错误页
                if (request.isForMainFrame() || url.equals(getUrl())) {
                    showReceivedErrorPage(view, error.getErrorCode(), error.getDescription().toString(), request.getUrl().toString());
                }
            }
        }

4. Solve the problem of white screen on the page:

When the SSL certificate is invalid, it will cause a white screen problem, you can  add handler.proceed() in the onReceivedSslError method ; 

Can solve the white screen problem:

        /**
         * 【解决白屏问题】
         * 如SSL证书无效时调用
         *
         * @param view
         * @param handler
         * @param error
         */
        @Override
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
            //此处处理可避免SSL证书无效的页面白屏
            handler.proceed();
            super.onReceivedSslError(view, handler, error);
            Log.d(TAG, "onReceivedSslError---> error = " + error);
        }

Guess you like

Origin blog.csdn.net/u012440207/article/details/121758583