美文网首页
Android的WebView的API详解

Android的WebView的API详解

作者: isLJli | 来源:发表于2021-03-08 10:56 被阅读0次

    WebView

    WebView是谷歌提供的一个加载H5的控件,WebView这个控件又包含四大部分:WebSettings、WebViewClient、WebChromeClient、JavascriptInterface。通过四个类,我们可以为WebView设置基础功能和监听,下面会逐一介绍四个类的方法。除了这四部分,WebView还有自己的API,先来介绍WebView的API

    WebView的API介绍

    列出WebView一些常用的API,如加载url、前进后退、清理缓存、状态管理更新

          // 加载url:
          webView.loadUrl(url)
    
          // 往请求头header增加参数
          val hashMap: HashMap<String, String> = HashMap()
          hashMap["name"] = "zhangsan"
          webView.loadUrl(url, hashMap)
    
          // 加载 HTML 页面的一小段内容
          webView.loadData("", "text/html", "utf-8")
          webView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null)
    
          // 后退、前进:
          webView.canGoBack() //是否可以后退
          webView.goBack() //后退一页面
          webView.canGoForward() //是否可以前进
          webView.goForward() //前进一页面
          webView.goBackOrForward(-1) //后退或前进多少步,正前负退
    
          //清除缓存数据:
          // 清除网页访问留下的缓存,由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.
          webView.clearCache(true)
          // 清除当前webview访问的历史记录,只会webview访问历史记录里的所有记录除了当前访问记录.
          webView.clearHistory()
          // 这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据。
          webView.clearFormData()
    
          //WebView的状态
          webView.onResume() // 可见状态
          webView.onPause() // 页面失去焦点变成不可见状态
          webView.pauseTimers() // 页面失去焦点变成不可见状态,对整个应用的webview起作用
          webView.resumeTimers() //恢复pauseTimers时的动作
          webView.destroy() //销毁
    
    

    WebSettings

    WebSettings类的主要作用是为H5设置一些配置功能,如常用设置是否支持JSjavaScriptEnabled、设置缓存cacheMode。如下是WebSettings的API介绍。

          webSetting = webView.settings
    
          // 告诉WebView启用JavaScript执行。
          webSetting?.javaScriptEnabled = true
    
          //缓存
          //缓存模式
          webSetting?.cacheMode = WebSettings.LOAD_DEFAULT
          // 开启 DOM storage API 功能
          webSetting?.domStorageEnabled = true
          //开启 database storage API 功能
          webSetting?.databaseEnabled = true
          //开启 Application Caches 功能,还需要设置路径
          webSetting?.setAppCacheEnabled(true)
          webSetting?.setAppCachePath("")
    
          //设置WebView是否应使用其屏幕上的缩放控件和手势支持缩放。
          //可以使用{@link #setBuiltInZoomControls}设置应使用的特定缩放机制。
          //此设置不会影响使用{@link WebView#zoomIn()}和{@link WebView#zoomOut()}方法执行的缩放。默认值为{@code true}。
          webSetting?.setSupportZoom(true)
          //设置WebView是否应使用其内置的缩放机制。内置的缩放机制包括屏幕缩放控件(显示在WebView的内容上)以及使用捏合手势控制缩放。可以使用{@link #setDisplayZoomControls}设置是否显示这些屏幕上的控件。默认值为{@code false}
          webSetting?.builtInZoomControls = false
    
          // 是否支持ViewPort的meta tag属性,
          // 如果页面有ViewPort meta tag 指定的宽度,则使用meta tag指定的值,否则默认使用宽屏的视图窗口
          webSetting?.useWideViewPort = true
          // 设置WebView是否以概述模式加载页面,即缩小内容以适合屏幕宽度。
          // 当内容宽度大于WebView控件的宽度时,例如{@link #getUseWideViewPort} 已启用。默认值为{@code false}
          webSetting?.loadWithOverviewMode = true
    
          //设置渲染线程的优先级。与其他设置不同,此设置每个过程仅需要调用一次。默认值为{@link RenderPriority#NORMAL}。
          webSetting?.setRenderPriority(WebSettings.RenderPriority.HIGH)
    
          // 设置基础布局算法。这将导致WebView的重新布局。默认值为{@link LayoutAlgorithm#NARROW_COLUMNS}。
          webSetting?.layoutAlgorithm = WebSettings.LayoutAlgorithm.NORMAL
     
          //启用或禁用WebView中的文件访问。
          //请注意,这仅启用或禁用文件系统访问。仍然可以使用file:/// android_asset和file:/// android_res访问资产和资源。
          webSetting?.allowFileAccess = true
    
          //告诉WebView在调用{@link WebView#requestFocus(int,android.graphics.Rect)}时是否需要将节点设置为具有焦点。默认值为{@code true}。
          webSetting?.setNeedInitialFocus(true)
    
          //告诉JavaScript自动打开窗口。
          // 这适用于JavaScript函数{@code window.open()}。默认值为{@code false}
          webSetting?.javaScriptCanOpenWindowsAutomatically = true
    
          //支持自动加载图片
          webSetting?.loadsImagesAutomatically = true
    
          //设置解码html页面时使用的默认文本编码名称。 *默认值为“ UTF-8”。
          webSetting?.defaultTextEncodingName = "utf-8"
    
          // 设置默认字体大小。默认值为16。
          webSetting?.defaultFontSize = 16
    
          //设置最小字体大小。预设值为8。
          webSetting?.minimumFontSize = 8
    
          /**
           *  Webview在安卓5.0之前默认允许其加载混合网络协议内容
           *  在安卓5.0之后,默认不允许加载http与https混合内容,需要设置webview允许其加载混合网络协议内容
           */
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
              webSetting?.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
          }
    
          //获取WebView是否支持多个窗口。
          webSetting?.supportMultipleWindows()
    
    

    在项目中一般设置这些WebSettings的API就行,当然一些特殊需求场景可以自己另外设置。

      // webSettings的通用设置
      private fun initWebSettings(webView: WebView?) {
          webView?.isEmpty() ?: return
    
          val webSettings = webView.settings
    
          webSettings.javaScriptEnabled = true //支持JS
    
          webSettings.useWideViewPort =
              true // 如果页面有ViewPort meta tag 指定的宽度,则使用meta tag指定的值,否则默认使用宽屏的视图窗口
          webSettings.loadWithOverviewMode =
              true //即缩小内容以适合屏幕宽度。当内容宽度大于WebView控件的宽度时,例如{@link #getUseWideViewPort} 已启用
    
          webSettings.cacheMode = WebSettings.LOAD_DEFAULT //缓存模式
          webSettings.domStorageEnabled = true //开启 DOM storage API 功能
          webSettings.databaseEnabled = true //开启 database storage API 功能
          webSettings.setAppCacheEnabled(true) //开启 Application Caches 功能
          webSettings.setAppCachePath(filesDir.absolutePath + "webcache") //设置  Application Caches 缓存目录
          webSettings.databasePath = filesDir.absolutePath + "webcache" //设置数据库缓存路径
    
          webSettings.setRenderPriority(WebSettings.RenderPriority.HIGH) //设置渲染线程的优先级。
    
          webSettings.layoutAlgorithm = WebSettings.LayoutAlgorithm.NARROW_COLUMNS //适应内容大小
    
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
              // 允许HTTPS和HTTP一起加载
              webSettings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
          }
    
          // 为了处理一些WebView使用而被检测到的漏洞
          try {
              // 设置WebView是否应保存密码。默认值为{@code true}。  @deprecated在将来的版本中将不支持在WebView中保存密码。
              webSettings.savePassword = false
              // 同源绕过策略
              webSettings.allowFileAccess = false
              // 移除有风险的webView系统隐藏接口漏洞
              webView.removeJavascriptInterface("searchBoxJavaBridge_")
              webView.removeJavascriptInterface("accessibility")
              webView.removeJavascriptInterface("accessibilityTraversal")
          } catch (e: Exception) {
              e.printStackTrace()
          }
    
          //自定义UA
          val ua = webSettings.userAgentString
          if (ua.isNotEmpty() && ua.endsWith("自定义")) {
              return
          }
          webSettings.userAgentString = ua + "自定义"
          
      }
    
    

    WebViewClient

    WebViewClient的作用是处理各种通知和请求事件。比较常用的方法有shouldOverrideUrlLoading、onPageStarted、onPageFinished。以下是各方法讲解:

    class BaseWebViewClient : WebViewClient {
    
      override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
          //在网页上的所有加载都经过这个方法
          //比如在本地WebView加载H5,而不是浏览器
          return super.shouldOverrideUrlLoading(view, request)
      }
    
      override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
          super.onPageStarted(view, url, favicon)
          //这个事件就是开始载入页面调用的,我们可以设定一个loading的页面,告诉用户程序在等待网络响应。
      }
    
      override fun onPageFinished(view: WebView?, url: String?) {
          super.onPageFinished(view, url)
          //在页面加载结束时调用。同样道理,我们可以关闭loading 条,切换程序动作。
      }
    
      override fun onReceivedError(
          view: WebView?,
          request: WebResourceRequest?,
          error: WebResourceError?
      ) {
          super.onReceivedError(view, request, error)
          // (报告错误信息,当加载出错的时候会回调此方法)
      }
    
      override fun shouldInterceptRequest(
          view: WebView?,
          request: WebResourceRequest?
      ): WebResourceResponse? {
          // 拦截替换网络请求数据
          return super.shouldInterceptRequest(view, request)
      }
    
      override fun shouldOverrideKeyEvent(view: WebView?, event: KeyEvent?): Boolean {
          //重写此方法才能够处理在浏览器中的按键事件。
          return super.shouldOverrideKeyEvent(view, event)
      }
    
      override fun onLoadResource(view: WebView?, url: String?) {
          super.onLoadResource(view, url)
          // 在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。
      }
    
      override fun doUpdateVisitedHistory(view: WebView?, url: String?, isReload: Boolean) {
          super.doUpdateVisitedHistory(view, url, isReload)
          //(更新历史记录)
      }
    
      override fun onFormResubmission(view: WebView?, dontResend: Message?, resend: Message?) {
          super.onFormResubmission(view, dontResend, resend)
          //(应用程序重新请求网页数据)
      }
    
      override fun onReceivedHttpAuthRequest(
          view: WebView?,
          handler: HttpAuthHandler?,
          host: String?,
          realm: String?
      ) {
          super.onReceivedHttpAuthRequest(view, handler, host, realm)
          //(通知主机应用程序WebView收到HTTP身份验证请求。主机应用程序可以使用提供的* {@link HttpAuthHandler}设置WebView对请求的响应。 *默认行为是取消请求
      }
    
      override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
          super.onReceivedSslError(view, handler, error)
          //SSL过程发生错误,重写此方法可以让webview处理https请求。
      }
    
      override fun onScaleChanged(view: WebView?, oldScale: Float, newScale: Float) {
          super.onScaleChanged(view, oldScale, newScale)
          // (WebView缩放大小发生改变时调用)
      }
    
      override fun onUnhandledKeyEvent(view: WebView?, event: KeyEvent?) {
          super.onUnhandledKeyEvent(view, event)
          //(Key事件未被加载时调用)
      }
    
    }
    

    项目中WebViewClient的通用配置:

     webView?.webViewClient = CommonWebViewClient(this)
    
      private class CommonWebViewClient(val context: Context) : WebViewClient() {
    
          override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
              url ?: return false
              try {
                  if (!url.startsWith("http://") && !url.startsWith("https://")) {
                      val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
                      context.startActivity(intent)
                  }
              } catch (e: Exception) {
                  return true
              }
              return super.shouldOverrideUrlLoading(view, url)
          }
    
          override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
              super.onPageStarted(view, url, favicon)
              // 开始Loading页面
          }
    
          override fun onPageFinished(view: WebView?, url: String?) {
              super.onPageFinished(view, url)
              // 开始结束Loading页面
          }
    
          override fun onReceivedError(
              view: WebView?,
              request: WebResourceRequest?,
              error: WebResourceError?
          ) {
              super.onReceivedError(view, request, error)
              // 开始结束Loading页面
          }
      }
    

    WebChromeClient

    WebChromeClient是内核处理类,主要用于网站的加载进度、标题、图片文件选择、JS弹窗

    class BaseWebChromeClient : WebChromeClient {
    
      override fun onProgressChanged(view: WebView?, newProgress: Int) {
          super.onProgressChanged(view, newProgress)
          //当前网页加载的进度
      }
    
      override fun onReceivedTitle(view: WebView?, title: String?) {
          super.onReceivedTitle(view, title)
          //网页title标题
      }
    
      override fun onReceivedIcon(view: WebView?, icon: Bitmap?) {
          super.onReceivedIcon(view, icon)
          //网页图标
      }
    
      override fun onShowFileChooser(
          webView: WebView?,
          filePathCallback: ValueCallback<Array<Uri>>?,
          fileChooserParams: FileChooserParams?
      ): Boolean {
          //告诉客户端显示文件选择器。响应于用户按下“选择文件”按钮,调用它来处理具有“文件”输入类型的HTML表单。
          // 要取消请求,请调用<code> filePathCallback.onReceiveValue(null)</ code>并返回{@code true}。
          return super.onShowFileChooser(webView, filePathCallback, fileChooserParams)
      }
    
      override fun onShowCustomView(view: View?, callback: CustomViewCallback?) {
          super.onShowCustomView(view, callback)
          // 通知主机应用程序当前页面已进入全屏模式。调用之后,Web内容将不再在WebView中呈现,而是在{@code view}中呈现。
          // 主机应用程序应将此视图添加到配置了{@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN}标志的窗口中,以便实际全屏显示此Web内容。
      }
    
      override fun onHideCustomView() {
          super.onHideCustomView()
          // 通知主机应用程序当前页面已退出全屏模式。
          // 宿主应用程序必须隐藏自定义视图(以前传递给{@link #onShowCustomView(View,CustomViewCallback)onShowCustomView()}的视图)。调用之后,Web内容将再次在原始WebView中呈现
      }
    
      override fun getDefaultVideoPoster(): Bitmap? {
          //webview视频未播放时默认显示占位图
          return super.getDefaultVideoPoster()
      }
    
      //通知主机应用程序该网页要显示JavaScript {@code alert()}对话框。
      //<p>如果此方法返回{@code false}或未被覆盖,则默认行为是显示一个包含警报消息的对话框并挂起JavaScript的执行,直到关闭对话框为止。
      //<p>要显示自定义对话框,应用应通过此方法返回{@code true},在这种情况下,默认对话框将不会显示,并且JavaScript 执行将被暂停。
      // 该应调用{@code JsResult.confirm()}(关闭自定义对话框时),以便可以恢复执行JavaScript。
      //<p>要取消显示对话框并允许JavaScript执行继续,请立即调用{@code JsResult.confirm()},然后返回{@code true}。
      //<p>请注意,如果将{@link WebChromeClient}设置为{@code null},或者根本没有设置{@link WebChromeClient},则默认对话框将被取消,并且Javascript将立即继续执行。
      //<p>请注意,默认对话框不会从父窗口继承{@link android.view.Display#FLAG_SECURE}标志。
      override fun onJsAlert(
          view: WebView?,
          url: String?,
          message: String?,
          result: JsResult?
      ): Boolean {
          return super.onJsAlert(view, url, message, result)
      }
    
      // 通知主机应用程序该网页要显示JavaScript {@code Confirm()}对话框。
      //<p>如果此方法返回{@code false}或未重写,则默认行为是显示一个包含该消息的对话框并暂停JavaScript执行,直到该对话框被关闭为止。
      // 当用户按下“确认”按钮时,默认对话框将返回{@code true}到JavaScript {@code Confirm()}代码,而当用户按下“ cancel”时将返回{@code false}到JavaScript代码。 '按钮或关闭对话框。
      //<p>要显示自定义对话框,应用应通过此方法返回{@code true},在这种情况下,默认对话框将不会显示,并且JavaScript
      //执行将被暂停。该应用应调用 自定义对话框关闭时,{@ code JsResult.confirm()}或{@code JsResult.cancel()}。
      // <p>要取消显示对话框并允许JavaScript执行继续,请立即调用{@code JsResult.confirm()}或{@code JsResult.cancel()},然后返回{@code true}。
      //<p>请注意,如果将{@link WebChromeClient}设置为{@code null},或者根本没有设置{@link WebChromeClient},则会取消默认对话框,而默认值{@code false }将立即返回到JavaScript代码。
      //<p>请注意,默认对话框不会从父窗口继承{@link android.view.Display#FLAG_SECURE}标志。
      override fun onJsConfirm(
          view: WebView?,
          url: String?,
          message: String?,
          result: JsResult?
      ): Boolean {
          return super.onJsConfirm(view, url, message, result)
      }
    
      // 通知主机应用程序该网页要显示JavaScript {@code hint()}对话框。
      // <p>如果此方法返回{@code false}或未被覆盖,则默认行为是显示一个包含该消息的对话框并暂停JavaScript执行,直到该对话框被关闭为止。
      // 取消该对话框后,JavaScript {@code hint()}将返回用户键入的字符串,如果用户按下“取消”按钮,则返回null。
      //<p>要显示自定义对话框,应用应通过此方法返回{@code true},在这种情况下,默认对话框将不会显示,并且JavaScript 执行将被暂停。
      // 该应用应调用 取消自定义对话框后,{@ code JsPromptResult.confirm(result)}。
      //<p>要取消显示对话框并允许JavaScript执行继续,请立即调用{@code JsPromptResult.confirm(result)},然后返回{@code true}。
      //<p>请注意,如果将{@link WebChromeClient}设置为{@code null},或者根本没有设置{@link WebChromeClient},则默认对话框将被取消,并且将返回{@code null}立即添加到JavaScript代码。
      //<p>请注意,默认对话框不会从父窗口继承{@link android.view.Display#FLAG_SECURE}标志。
      override fun onJsPrompt(
          view: WebView?,
          url: String?,
          message: String?,
          defaultValue: String?,
          result: JsPromptResult?
      ): Boolean {
          return super.onJsPrompt(view, url, message, defaultValue, result)
      }
    
    }
    

    在项目中WebChromeClient的通用配置:

        // 给这个变量设置已选择的Uri
        var urlArrCallBack: ValueCallback<Array<Uri>>? = null
    
        webView?.webChromeClient = CommonWebChromeClient()
    
        private inner class CommonWebChromeClient : WebChromeClient() {
    
          // 网页加载的进度
          override fun onProgressChanged(view: WebView?, newProgress: Int) {
              if (newProgress >= 100) {
                  // 隐藏Loading页面
              }
          }
    
          // 设置标题
          override fun onReceivedTitle(view: WebView?, title: String?) {
              super.onReceivedTitle(view, title)
              titleNameTv.text = title
          }
    
          // 跳用图片和文件选择器
          override fun onShowFileChooser(
              webView: WebView?,
              filePathCallback: ValueCallback<Array<Uri>>?,
              fileChooserParams: FileChooserParams?
          ): Boolean {
              urlArrCallBack = filePathCallback
              // showDialog() 展示图片/文件选择器
              return true
          }
    
          override fun onJsAlert(
              view: WebView?,
              url: String?,
              message: String?,
              result: JsResult?
          ): Boolean {
              // 展示弹窗
              AlertDialog.Builder(this@CommonWebViewActivity)
                  .setTitle("JsAlert")
                  .setMessage(message)
                  .setPositiveButton(
                      "OK"
                  ) { dialog, which -> result?.confirm() }
                  .setCancelable(false)
                  .show()
              return true
          }
    
          override fun onJsConfirm(
              view: WebView?,
              url: String?,
              message: String?,
              result: JsResult?
          ): Boolean {
              // 展示弹窗
              AlertDialog.Builder(this@CommonWebViewActivity)
                  .setTitle("JsConfirm")
                  .setMessage(message)
                  .setPositiveButton(
                      "OK"
                  ) { dialog, which -> result?.confirm() }
                  .setNegativeButton(
                      "Cancel"
                  ) { dialog, which -> result?.cancel() }
                  .setCancelable(false)
                  .show()
    
              return true
          }
    
          override fun onJsPrompt(
              view: WebView?,
              url: String?,
              message: String?,
              defaultValue: String?,
              result: JsPromptResult?
          ): Boolean {
              val et = EditText(this@CommonWebViewActivity)
              et.setText(defaultValue)
              AlertDialog.Builder(this@CommonWebViewActivity)
                  .setTitle(message)
                  .setView(et)
                  .setPositiveButton(
                      "OK"
                  ) { dialog, which -> result?.confirm(et.text.toString()) }
                  .setNegativeButton(
                      "Cancel"
                  ) { dialog, which -> result?.cancel() }
                  .setCancelable(false)
                  .show()
              return true
          }
    
      }
    
    

    JavascriptInterface

    可以通过addJavascriptInterface()设置JavascriptInterface,从而使JS可以直接调用JavascriptInterface设置好的方法,比如H5想要跳转Activity、打开原生视频等都可以通知App。

      webView?.addJavascriptInterface(CommonWebJsInterface(), "className")
    
      val MSG_OPEN_ACTIVITY = 1
    
      var UIHandler: UIHandle = UIHandle()
    
      class UIHandle : Handler() {
    
          private var commonWebViewActivity: CommonWebViewActivity? = null
    
          private var jsonStr: String? = null
    
          fun setJsonStr(jsonStr: String?) {
              this.jsonStr = jsonStr
          }
    
          override fun handleMessage(msg: Message) {
              if (commonWebViewActivity == null || commonWebViewActivity?.isFinishing == true) {
                  removeCallbacksAndMessages(null)
                  return
              }
              commonWebViewActivity?.wrapHandleMessage(msg,jsonStr)
          }
      }
    
      private fun wrapHandleMessage(msg: Message?, jsonStr: String?) {
          msg ?: return
          if (msg.what == MSG_OPEN_ACTIVITY) {
              startActivity(Intent(this, WebViewActivity::class.java))
          }
      }
    
      inner class CommonWebJsInterface : WebJsInterface {
    
          // JS调用方法跳转Activity
          @JavascriptInterface
          override fun openActivity(jsonStr: String?) {
              UIHandler.setJsonStr(jsonStr)
              val msg = Message.obtain()
              msg.what = MSG_OPEN_ACTIVITY
              UIHandler.sendMessage(msg)
          }
    
          // JS调用方法播放视频
          @JavascriptInterface
          override fun playVideo(jsonStr: String?) {
    
          }
    
      }
    
      private interface WebJsInterface {
    
          @JavascriptInterface
          fun openActivity(jsonStr: String?)
    
          @JavascriptInterface
          fun playVideo(jsonStr: String?)
    
      }
    

    当然安卓也可以通过evaluateJavascript调用JS的方法。

    简单模版代码:

    class CommonWebViewActivity : AppCompatActivity() {
    
      var webView: WebView? = null
    
      // 给这个变量设置已选择的Uri
      var urlArrCallBack: ValueCallback<Array<Uri>>? = null
    
      override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
          setContentView(R.layout.common_webview_activity)
    
          containerFrameLayout.removeAllViews()
          webView = WebView(applicationContext)
          containerFrameLayout.addView(webView)
    
          initWebSettings(webView)
    
          webView?.webViewClient = CommonWebViewClient(this)
    
          webView?.webChromeClient = CommonWebChromeClient()
    
          webView?.addJavascriptInterface(CommonWebJsInterface(), "className")
      }
    
      // webSettings的通用设置
      private fun initWebSettings(webView: WebView?) {
          webView?.isEmpty() ?: return
    
          val webSettings = webView.settings
    
          webSettings.javaScriptEnabled = true //支持JS
    
          webSettings.useWideViewPort =
              true // 如果页面有ViewPort meta tag 指定的宽度,则使用meta tag指定的值,否则默认使用宽屏的视图窗口
          webSettings.loadWithOverviewMode =
              true //即缩小内容以适合屏幕宽度。当内容宽度大于WebView控件的宽度时,例如{@link #getUseWideViewPort} 已启用
    
          webSettings.cacheMode = WebSettings.LOAD_DEFAULT //缓存模式
          webSettings.domStorageEnabled = true //开启 DOM storage API 功能
          webSettings.databaseEnabled = true //开启 database storage API 功能
          webSettings.setAppCacheEnabled(true) //开启 Application Caches 功能
          webSettings.setAppCachePath(filesDir.absolutePath + "webcache") //设置  Application Caches 缓存目录
          webSettings.databasePath = filesDir.absolutePath + "webcache" //设置数据库缓存路径
    
          webSettings.setRenderPriority(WebSettings.RenderPriority.HIGH) //设置渲染线程的优先级。
    
          webSettings.layoutAlgorithm = WebSettings.LayoutAlgorithm.NARROW_COLUMNS //适应内容大小
    
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
              // 允许HTTPS和HTTP一起加载
              webSettings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
          }
    
          // 为了处理一些WebView使用而被检测到的漏洞
          try {
              // 设置WebView是否应保存密码。默认值为{@code true}。  @deprecated在将来的版本中将不支持在WebView中保存密码。
              webSettings.savePassword = false
              // 同源绕过策略
              webSettings.allowFileAccess = false
              // 移除有风险的webView系统隐藏接口漏洞
              webView.removeJavascriptInterface("searchBoxJavaBridge_")
              webView.removeJavascriptInterface("accessibility")
              webView.removeJavascriptInterface("accessibilityTraversal")
          } catch (e: Exception) {
              e.printStackTrace()
          }
    
          //自定义UA
          val ua = webSettings.userAgentString
          if (ua.isNotEmpty() && ua.endsWith("自定义")) {
              return
          }
          webSettings.userAgentString = ua + "自定义"
    
      }
    
      private inner class CommonWebViewClient(val context: Context) : WebViewClient() {
    
          override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
              url ?: return false
              try {
                  if (!url.startsWith("http://") && !url.startsWith("https://")) {
                      val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
                      context.startActivity(intent)
                  }
              } catch (e: Exception) {
                  return true
              }
              return super.shouldOverrideUrlLoading(view, url)
          }
    
          override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
              super.onPageStarted(view, url, favicon)
              // 开始Loading页面
          }
    
          override fun onPageFinished(view: WebView?, url: String?) {
              super.onPageFinished(view, url)
              // 开始结束Loading页面
          }
    
          override fun onReceivedError(
              view: WebView?,
              request: WebResourceRequest?,
              error: WebResourceError?
          ) {
              super.onReceivedError(view, request, error)
              // 开始结束Loading页面
          }
      }
    
      private inner class CommonWebChromeClient : WebChromeClient() {
    
          // 网页加载的进度
          override fun onProgressChanged(view: WebView?, newProgress: Int) {
              if (newProgress >= 100) {
                  // 隐藏Loading页面
              }
          }
    
          // 设置标题
          override fun onReceivedTitle(view: WebView?, title: String?) {
              super.onReceivedTitle(view, title)
              titleNameTv.text = title
          }
    
          // 跳用图片和文件选择器
          override fun onShowFileChooser(
              webView: WebView?,
              filePathCallback: ValueCallback<Array<Uri>>?,
              fileChooserParams: FileChooserParams?
          ): Boolean {
              urlArrCallBack = filePathCallback
              // showDialog() 展示图片/文件选择器
              return true
          }
    
          //通知主机应用程序该网页要显示JavaScript {@code alert()}对话框。
          //<p>如果此方法返回{@code false}或未被覆盖,则默认行为是显示一个包含警报消息的对话框并挂起JavaScript的执行,直到关闭对话框为止。
          //<p>要显示自定义对话框,应用应通过此方法返回{@code true},在这种情况下,默认对话框将不会显示,并且JavaScript 执行将被暂停。
          // 该应调用{@code JsResult.confirm()}(关闭自定义对话框时),以便可以恢复执行JavaScript。
          //<p>要取消显示对话框并允许JavaScript执行继续,请立即调用{@code JsResult.confirm()},然后返回{@code true}。
          //<p>请注意,如果将{@link WebChromeClient}设置为{@code null},或者根本没有设置{@link WebChromeClient},则默认对话框将被取消,并且Javascript将立即继续执行。
          //<p>请注意,默认对话框不会从父窗口继承{@link android.view.Display#FLAG_SECURE}标志。
          override fun onJsAlert(
              view: WebView?,
              url: String?,
              message: String?,
              result: JsResult?
          ): Boolean {
              // 展示弹窗
              AlertDialog.Builder(this@CommonWebViewActivity)
                  .setTitle("JsAlert")
                  .setMessage(message)
                  .setPositiveButton(
                      "OK"
                  ) { dialog, which -> result?.confirm() }
                  .setCancelable(false)
                  .show()
              return true
          }
    
          // 通知主机应用程序该网页要显示JavaScript {@code Confirm()}对话框。
          //<p>如果此方法返回{@code false}或未重写,则默认行为是显示一个包含该消息的对话框并暂停JavaScript执行,直到该对话框被关闭为止。
          // 当用户按下“确认”按钮时,默认对话框将返回{@code true}到JavaScript {@code Confirm()}代码,而当用户按下“ cancel”时将返回{@code false}到JavaScript代码。 '按钮或关闭对话框。
          //<p>要显示自定义对话框,应用应通过此方法返回{@code true},在这种情况下,默认对话框将不会显示,并且JavaScript
          //执行将被暂停。该应用应调用 自定义对话框关闭时,{@ code JsResult.confirm()}或{@code JsResult.cancel()}。
          // <p>要取消显示对话框并允许JavaScript执行继续,请立即调用{@code JsResult.confirm()}或{@code JsResult.cancel()},然后返回{@code true}。
          //<p>请注意,如果将{@link WebChromeClient}设置为{@code null},或者根本没有设置{@link WebChromeClient},则会取消默认对话框,而默认值{@code false }将立即返回到JavaScript代码。
          //<p>请注意,默认对话框不会从父窗口继承{@link android.view.Display#FLAG_SECURE}标志。
          override fun onJsConfirm(
              view: WebView?,
              url: String?,
              message: String?,
              result: JsResult?
          ): Boolean {
              // 展示弹窗
              AlertDialog.Builder(this@CommonWebViewActivity)
                  .setTitle("JsConfirm")
                  .setMessage(message)
                  .setPositiveButton(
                      "OK"
                  ) { dialog, which -> result?.confirm() }
                  .setNegativeButton(
                      "Cancel"
                  ) { dialog, which -> result?.cancel() }
                  .setCancelable(false)
                  .show()
    
              return true
          }
    
          // 通知主机应用程序该网页要显示JavaScript {@code hint()}对话框。
          // <p>如果此方法返回{@code false}或未被覆盖,则默认行为是显示一个包含该消息的对话框并暂停JavaScript执行,直到该对话框被关闭为止。
          // 取消该对话框后,JavaScript {@code hint()}将返回用户键入的字符串,如果用户按下“取消”按钮,则返回null。
          //<p>要显示自定义对话框,应用应通过此方法返回{@code true},在这种情况下,默认对话框将不会显示,并且JavaScript 执行将被暂停。
          // 该应用应调用 取消自定义对话框后,{@ code JsPromptResult.confirm(result)}。
          //<p>要取消显示对话框并允许JavaScript执行继续,请立即调用{@code JsPromptResult.confirm(result)},然后返回{@code true}。
          //<p>请注意,如果将{@link WebChromeClient}设置为{@code null},或者根本没有设置{@link WebChromeClient},则默认对话框将被取消,并且将返回{@code null}立即添加到JavaScript代码。
          //<p>请注意,默认对话框不会从父窗口继承{@link android.view.Display#FLAG_SECURE}标志。
          override fun onJsPrompt(
              view: WebView?,
              url: String?,
              message: String?,
              defaultValue: String?,
              result: JsPromptResult?
          ): Boolean {
              val et = EditText(this@CommonWebViewActivity)
              et.setText(defaultValue)
              AlertDialog.Builder(this@CommonWebViewActivity)
                  .setTitle(message)
                  .setView(et)
                  .setPositiveButton(
                      "OK"
                  ) { dialog, which -> result?.confirm(et.text.toString()) }
                  .setNegativeButton(
                      "Cancel"
                  ) { dialog, which -> result?.cancel() }
                  .setCancelable(false)
                  .show()
              return true
          }
    
      }
    
      val MSG_OPEN_ACTIVITY = 1
    
      var UIHandler: UIHandle? = UIHandle()
    
      class UIHandle : Handler() {
    
          private var commonWebViewActivity: CommonWebViewActivity? = null
    
          private var jsonStr: String? = null
    
          fun setJsonStr(jsonStr: String?) {
              this.jsonStr = jsonStr
          }
    
          override fun handleMessage(msg: Message) {
              if (commonWebViewActivity == null || commonWebViewActivity?.isFinishing == true) {
                  removeCallbacksAndMessages(null)
                  return
              }
              commonWebViewActivity?.wrapHandleMessage(msg, jsonStr)
          }
      }
    
      private fun wrapHandleMessage(msg: Message?, jsonStr: String?) {
          msg ?: return
          if (msg.what == MSG_OPEN_ACTIVITY) {
              startActivity(Intent(this, WebViewActivity::class.java))
          }
      }
    
      inner class CommonWebJsInterface : WebJsInterface {
    
          // JS调用方法跳转Activity
          @JavascriptInterface
          override fun openActivity(jsonStr: String?) {
              UIHandler?.setJsonStr(jsonStr)
              val msg = Message.obtain()
              msg.what = MSG_OPEN_ACTIVITY
              UIHandler?.sendMessage(msg)
          }
    
          // JS调用方法播放视频
          @JavascriptInterface
          override fun playVideo(jsonStr: String?) {
    
          }
    
      }
    
      private interface WebJsInterface {
    
          @JavascriptInterface
          fun openActivity(jsonStr: String?)
    
          @JavascriptInterface
          fun playVideo(jsonStr: String?)
    
      }
    
    
      override fun onResume() {
          super.onResume()
          webView?.onResume()
      }
    
      override fun onPause() {
          super.onPause()
          webView?.onPause()
      }
    
      override fun onDestroy() {
          try {
              onWebViewDestroy()
          } catch (e: java.lang.Exception) {
              e.printStackTrace()
          }
          super.onDestroy()
          UIHandler?.removeCallbacksAndMessages(null)
          UIHandler = null
      }
    
    
      /**
       * 销毁WebView;
       */
      private fun onWebViewDestroy() {
          if (webView != null) {
              webView?.webChromeClient = null
              webView?.loadDataWithBaseURL(null, "", "text/html", "utf-8", null)
              webView?.clearHistory()
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                  if (webView?.parent != null) {
                      (webView?.parent as ViewGroup).removeView(webView)
                  }
                  webView?.removeAllViews()
                  webView?.destroy()
              } else {
                  webView?.removeAllViews()
                  webView?.destroy()
                  if (webView?.parent != null) {
                      (webView?.parent as ViewGroup).removeView(webView)
                  }
              }
              webView = null
          }
      }
    
      /**
       * 条件允许时,按下返回键可以在WebView中返回上一页;
       */
      override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
          //WebView内导航
          if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_DOWN) {
              if (onBackInWebClicked()) {
                  return true
              }
          }
          return super.onKeyDown(keyCode, event)
      }
    
      /**
       * @return 是否处理的事件
       */
      protected fun onBackInWebClicked(): Boolean {
          //WebView内导航
          if (canWebViewGoBack() && webView != null && webView?.canGoBack() == true) {
              webView?.goBack()
              return true
          }
          return false
      }
    
      /**
       * @return 是否可以在WebView内进行返回导航
       */
      protected fun canWebViewGoBack(): Boolean {
          return true
      }
    
    }
    
    

    相关文章

      网友评论

          本文标题:Android的WebView的API详解

          本文链接:https://www.haomeiwen.com/subject/hfwofltx.html