Android WebView の開発 (1): 基本的なアプリケーション

1. Android WebView の開発 (1): 基本的なアプリケーション
2. Android WebView の開発 (2): WebView とネイティブの相互作用
3. Android WebView の開発 (3): WebView のパフォーマンスの最適化
4. Android WebView の開発 (4): WebView の独立したプロセス ソリューション
5 、Android WebView の開発 (5): カスタム WebView ツールバー

添付の GitHub ソース コード: WebViewExplore


1.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 は音楽の再生をサポートします

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

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

3. WebView による動画再生のサポート

Android WebView で動画を再生 (全画面再生を含む)

4. WebChrome クライアント

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

具体的な取材方法は以下の通りです。 

    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());

具体的な取材方法は以下の通りです。 

    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();
                }
            }
        }
    }

特にそれらを使用する場合は、次の方法もあります。

1. リダイレクトの問題:

shouldOverrideUrlLoadingメソッドを使用して、リダイレクトを判断および処理できます。

        /**
         * 重定向分析:
         *
         * @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);
        }

 WebView リダイレクトについて考慮する必要があるケースは次のとおりです。

  • 1. 最も一般的な http URL です [.doc、.apk、その他のダウンロード URL を除く]
  • 2. ダウンロードした http URL [.doc .apk など]
  • 3. http または https 以外のカスタム URL [weixin:// alipays:// など]
  • [非推奨] Web ページを開いたときにアプリが自動的にアクティブ化されないようにする場合は、request.hasGesture() をクリックすることで判断できます [true] の場合、サードパーティ製アプリがアクティブ化されます。(このスキームはあまり正確でない場合があるため、次のスキームを使用できます)
  • [推奨] isClickWeb = false のようなブール値を定義し、onTouchEvent の DOWN メソッドで true に割り当てます。必要な位置に判定を追加するだけ [詳細はコードを参照]
        /**
         * 自定义重定向处理方法
         * @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. プリロードを実現する:

リソースのプリロードはshouldInterceptRequestメソッド で実装できます。

        /**
         * 【实现预加载】
         * 有时候一个页面资源比较多,图片,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. エラー ページの表示制限を増やします。

onReceivedErrorメソッドでは request.isForMainFrame() || url.equals(getUrl()を使用して、エラー ページの表示をできるだけ少なくします。つまり、エラー ページは、エラー ページがメイン ページの場合にのみ表示されます。ページ全体を回避する アイコンなどが正しく表示されず、ページ全体に影響を与える [NetEase Music の一部の URL など、このような状況が発生しており、この方法でエラー ページの表示を回避できます]。

        /**
         * 此方法中加载错误页面的时候,需要判断下 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. ページの白い画面の問題を解決します。

SSL 証明書が無効な場合、白い画面の問題が発生します。  onReceivedSslErrorメソッドに handler.proceed()を追加できます。

白い画面の問題を解決できます:

        /**
         * 【解决白屏问题】
         * 如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);
        }

おすすめ

転載: blog.csdn.net/u012440207/article/details/121758583