Android中WebView - litonghui/TechBlog GitHub Wiki

WebView 作为H5加载的组件对每一位移动开发者都不是很默认,项目中或多或少的也用到了,但是真实了解有多少哪?下面从WebView 优点、H5 加载过程、Js 与 Java 相互通信、WebViewClient 和 WebChromeClient 以及常用的重要方法、开发中需要注意的问题这几个方面聊聊WebView 。

一,WebView 特点

移动端开发软件发布方式是以打包为主,包括Android 的APK、iOS 的IPA、WindowsPhone 的XAP等,这要发布形式相比于网页开发,新功能更新和软件修复及时性较差,虽然针对移动端软件修复不同系统都有各自的修复,比如基于Android 的Dexposed、ClassLoader,插件化都不是很成熟和方便,WebView 相比较可以更好的做到新功能发布和软件修复,当然更加推荐使用Hybrid 和 React Native 。

二,H5 加载

 -------AndroidManifest-------

 <uses-permission android:name="android.permission.INTERNET" />

 -------Layout.xml-----------

 <WebView
    android:id="@+id/webview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_above="@id/back_button" >
 </WebView>

 -------XXXXActivity.java-----------

  mWebView = (WebView) findViewById(R.id.webview);
  if (!TextUtils.isEmpty(url)) {
       mWebView.loadUrl(url);
  }

三,JavaScript 与 Java

JavaScript 调用 Java 代码:
 -------JavaScript.js-----------

   <script type="text/javascript">
      function setPageTitle(title){
            window.webpage.onSetTitle(title);
         }
    </script>

 -------XXXXActivity.java-----------

    WebSettings webSettings = mWebView.getSettings();
    webSettings.setJavaScriptEnabled(true);
    JsInterface jsInterface = new JsInterface();
    mWebView.addJavascriptInterface(jsInterface, "webpage");


    public class JsInterface implements NonProguard{
     @JavascriptInterface
        public void onSetTitle(String title) {}
    }

    JsInterface 继承接口NonProguard ,在代码混淆时候,记得-keep class com.android.interfaces.NonProguard,否则JsInterface 类被混淆之后JavaScript 找不到该类,里面方法也就查找失败
Java 调用 JavaScript 代码:
   Android 开发切忌UI 线程被阻塞,因此不能再UI线程操作js,推荐使用方法如下:
   mHandler.postDelayed(new Runnable() {
       @Override
       public void run() {
       mWebView.loadUrl("javascript:wave()");
   }

  当然在Android 4.4 中提供了方法:evaluateJavascript
  mWebView.evaluateJavascript(script, new ValueCallback<String>() {
      @Override
      public void onReceiveValue(String value) {
      //TODO
      }
  });

四,重要方法

  • WebViewClient,从自带方法中,可以理解为在页面加载过程中请求时间,处理通知等.重定向WebSite

      public void onPageStarted(WebView view, String url, Bitmap favicon){ 
         super.onPageStarted(view, url, favicon);
      }  // 页面开始加载,可以设置加载动画 
    
      public boolean shouldOverrideUrlLoading(WebView view, String url) {
         if (Uri.parse(url).getHost().equals("www.example.com")) {
          // This is my web site, so do not override; let my WebView load the page
          return false; // 避免重定向问题,需要 return false
         }
         // Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
         Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
         startActivity(intent);
        return true;
      }
    
      onPageFinished(WebView view, String url)
    
      .....
    
  • WebChromeClient ,在网页加载网页的渲染,图标展示,js 交互。

      public boolean onJsAlert(WebView view, String url, String message,
                  JsResult result) {} // js 通过该方法确认弹窗
    
      public void onProgressChanged(WebView view, int newProgress) {} //显示更新加载进度
    
  • DownloadListener检测来自H5的下载,通过监听下载可以启动浏览器下载或者拦截Url 本地下载:

      mWebView.setDownloadListener(new DownloadListener() {
          @Override
          public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
              if(!TextUtils.isEmpty(url)&& Utils.isApk(url)) {
                  MyDownloadManager.getInstance().add(url);
                  Toast.makeText(mContext, "正在下载...", Toast.LENGTH_SHORT).show();
              }
          }
      });
    
      或者启动浏览器下载:
    
      mWebView.setDownloadListener(new DownloadListener() {
          @Override
          public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
                  Uri uri = Uri.parse(url);  
                  Intent intent = new Intent(Intent.ACTION_VIEW, uri);  
                  startActivity(intent);  
          }
      });
    

五,开发注意问题

  • 内存泄露,造成ANR
  • Js 漏洞,信息泄露

六,WebView 缓存,H5 会根据自己的需要使用LocalStorage,Android WebView 默认不支持storage,需要自己设置。

    webSettings.setDomStorageEnabled(true);
    webSettings.setAppCacheMaxSize(1024*1024*8);
    String appCachePath = getApplicationContext().getCacheDir().getAbsolutePath();
    webSettings.setAppCachePath(appCachePath);
    webSettings.setAllowFileAccess(true);
    webSettings.setAppCacheEnabled(true);

七,容错处理

八,Release 混淆

Js在调用Native 方法,如果该方法被混淆会调用失败,需要客户端在混淆配置中,具体如下:
    -keepattributes *Annotation*
    -keepattributes *JavascriptInterface*
    -keepclassmembers class * {
        @android.webkit.JavascriptInterface <methods>;
    }

九,网络图片加载策略

Html代码下载到WebView 后,webkit 开始解析网页各个节点,对于外部文件或者脚本文件会异步网络请求下载文件,解析image 节点,也势必会发起网络请求下载相应的图片,网络情况较差的情况下,过多的网络请求会造成带宽紧张,影响到css和js加载完成时间,造成loading过久。解决方法是高速webview先不要自动加载图片,等页面finish后再发起图片加载
        // 加快HTML网页加载完成速度
        if (Build.VERSION.SDK_INT >= 19) {
             webSettings.setLoadsImagesAutomatically(true);
        } else {
             webSettings.setLoadsImagesAutomatically(false);
       }

       @Override
       public void onPageFinished(WebView view, String url) {
           super.onPageFinished(view, url);
           if (!view.getSettings().getLoadsImagesAutomatically()) {
               view.getSettings().setLoadsImagesAutomatically(true);
           }
       }

未完待会...

⚠️ **GitHub.com Fallback** ⚠️