WebView 的反思和记录 ---定制设置和常见问题

WebView 的反思和记录 ---定制设置和常见问题

一些基本的内容就不提及了,下面主要记录在开发中尤其需要注意的内容

webview 自带接口的基本使用

要完成一定的自定义功能的webview,肯定就需要涉及到以下几个内容,WebSetting, WebViewClient, WebChromeClient, 它们可以让我们去定制一些内容.

WebSetting

初始化

    private void initWebSetting() {
        WebSetting webSettings = webView.getSettings();

        webSettings.setAllowContentAccess(true);
        webSettings.setAllowFileAccess(true);
        if (android.os.Build.VERSION.SDK_INT >= 16) {
            webSettings.setAllowFileAccessFromFileURLs(true);
            webSettings.setAllowUniversalAccessFromFileURLs(true);
        }

         // 启用应用缓存
        webSettings.setAppCacheEnabled(true);
        //缓存路径
        webSettings.setAppCachePath(getContext().getCacheDir().getAbsolutePath());
        // 比较重要,一共有四种模式,在下面说明
        webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);

        //开启数据库缓存和 DOM 缓存
        webSettings.setDatabaseEnabled(true);
        webSettings.setDomStorageEnabled(true);
        
        webSettings.setGeolocationDatabasePath(getContext().getFilesDir().toString());
        
        //支持缩放, 
        webSettings.setSupportZoom(true);
        //显示缩放按钮
        webSettings.setBuiltInZoomControls(true);
        webSettings.setDisplayZoomControls(false);
        webSettings.setLoadWithOverviewMode(true);
        webSettings.setTextZoom(100);
        webSettings.setUseWideViewPort(true);
        webSettings.setDefaultTextEncodingName("UTF-8");
        if (android.os.Build.VERSION.SDK_INT < 19) {
            webSettings.setLoadsImagesAutomatically(false);
        } else {
            webSettings.setLoadsImagesAutomatically(true);
        }

        webSettings.setBlockNetworkImage(false);
        
        // 支持运行 JS
        webSettings.setJavaScriptEnabled(true);
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
        webSettings.setGeolocationEnabled(true);
        webSettings.setSupportMultipleWindows(false);
        webSettings.setSaveFormData(false);
        webSettings.setLayoutAlgorithm(android.webkit.WebSettings.LayoutAlgorithm.NORMAL);
    }

其中比较重要的有一下:

  • setCacheMode()

    它有四种缓存模式:

    1. LOAD_DEFAULT:

      默认的缓存模式,在页面进行前进或后退时,如果有缓存可用并未过期, 就会优先加载缓存,否则,从网络上获取数据。

    2. LOAD_CACHE_ELSE_NETWORK:

      只要有缓存便会使用,哪怕它已经过期,如果缓存不可用,会从网络上获取数据。

      注:缓存的失效: 可能数据发生变化,还有可能是缓存的时间到期了,在浏览器中的header中的expires存储着数据的过期时间;

    3. LOAD_NO_CACHE: 不加载缓存,只从网络获取数据。

    4. LOAD_CACHE_ONLY: 只有缓存加载获取数据

    当然有时也可根据网络情况去设置:

        ConnectivityManager connectivityManager = (ConnectivityManager) context().
            getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
        if (networkInfo.isAvailable()) {
            webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
        } else {
            webSettings.setCacheMode(WebSettings.LOAD_CACHE_ONLY);
        }
    
  • setBlockNetworkImage(boolean flag)

    是否禁止从网络上加载图片, false 表示可以从网络上加载图片;

    注: 如果设置是从禁止到允许转变的话,图片数据并不会在设置改变后立即去获取, 而是在reload 时才会生效; 默认 flag = false.

WebViewClient

帮助处理webView的各种通知,事件;

设置如下:

webView.setWebViewClient(new WebViewClient() {
     @Override
     public void onPageStarted(WebView view, String url, Bitmap favicon) {
           Log.e(TAG, "onPageStarted url = " + url);
     }

     @Override
     public void onPageFinished(WebView view, String url) {
           Log.e(TAG, "onPageFinished");
     }

     @Override
     public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
          return false;
     }

     @Override
     public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
          super.onReceivedHttpError(view, request, errorResponse);
          Log.e(TAG, "onReceivedHttpError");
     }

     @Override
     public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
          super.onReceivedError(view, request, error);
          Log.e(TAG, "onReceivedError");         
     }
});

onPageStarted()

  • 会在网络加载时去调用,在这里可以做一些逻辑上的处理,例如,进度条的展示,以及进度数的设置:
progressBar.setVisibility(VISIBLE);
progressBar.setProgress(0);

很多时候,这个方法都会被调用不只一次,因为网址存在着重定向问题,所以会存在onpageStarted()不只被调用一次的情况,里面的逻辑处理也会被调用多次,编写时要注意争取保证里面的逻辑虽被调多次,但最好是只执行一次;加入一些防范机制。

  • 同时也可以在onpageStarted()方法里面进行一些判断,例如,当前webview是否可以回退、前进:
boolean canGoBack = webView.canGoBack();
boolean canGoForward = webView.canGoForward();

但是经实际的测试,发现有时在点击链接后,其实webview是可以返回的,但是canGoBackfalse, 后来经过不断测试,发现了有些网址的加载在进度为30% 左右以后时,webView.canGoBack(), 才会返回true,。

注: 具体可以在WebChromeClient 里的 onProgressChanged() 方法里测试。

onPageFinished()

会在网站加载结束后调用,在里面同样可以处理一些逻辑, 例如进度条逻辑 :

progressBar.setVisibility(GONE);

这里要注意,一定要把visibility 设置为 GONE, 若是设置为INVISIBILITY,则可能仍然会出现进度条加载到100% 后不消失的情况,要把其设置为GONE.


注意: 在一些低版本的某些手机上面,这个方法也会被调用多次,

shouldOverrideUrlLoading()

返回false为最好,慎重返回为true;

网络上一大堆说 ,返回true后才会使得网络的链接跳转由webview 处理, 返回false会调用系统浏览器去处理, 这种说法是错误的。

在官网上的说法是这样的:

  1. 如果没有提供 WebViewClient 对象, 则WebView 会请求 AM 选择系统的浏览器去加载;
  2. 提供了 WebViewClient 对象, 且shouldOverrideUrlLoading() 返回true , 则android 系统处理 URL;
  3. 提供了 WebViewClient 对象, 且shouldOverrideUrlLoading() 返回 false, 则当前 webview 处理URL;

并且,这个方法默认是返回 false, 因此我们不需要去重写这个方法, 只需要:

webView.setWebViewClient(new WebViewClient()...); 

便可实现利用webview去加载链接。

其他两个方法看意思便可知道如何使用。

WebChromeClient

WebChromeClient是辅助webView 处理javaScript 的对话框,网站图标, 网站title, 加载进度等 事件;

设置如下:

        webView.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                super.onProgressChanged(view, newProgress);
                progressBar.setProgress(newProgress);
            }

            @Override
            public void onReceivedTitle(WebView view, String title) {
                super.onReceivedTitle(view, title);
                //网站标题的处理
                webTitle = title.split(" ")[0];
            }
            
            @Override
            public void onReceivedIcon(WebView view, Bitmap icon) {
                super.onReceivedIcon(view, icon);   
            }
        });

onProgressChanged()

处理进度, 并且更新progressBar的进度;在这里设置去获取 webView.canGoBack(); 会发现在刚开始时,返回值为false, 当进度达到30左右时才开始返回true;

WebView 和ProgressBar 连用

很多时候,都需要一边展示加载,一边显示加载的进度,这时,我们可以把WebView 和ProgressBar 放在一个LinearLayout里面,这样更加方便去操作这两者。

例如:

public class ProgressWebView extends LinearLayout {
    //自定义ProgressWebView 继承与LinearLayout 
    //在里面实现对这两个view的绘制和加载;
    public ProgressWebView(Context context, @Nullable AttributeSet attrs) {
        //通过addView() 分别把二者加进去
        ...
        ...
        
        init();
        initWebSettings();
        initWebListener();
    }
    
    //初始化一些参数
    private void init() {
        ...
    }
    
    //初始化webSettings 
    private void initWebSettings() {
        ...
    }
    
    //初始化webListener
    private void initWebListener() {
        ...
        webView.setWebViewClient(...);
        webView.setWebChromeClient(...);
    }
}

注意:加载完成时,要将 progressBar 设置为 setVisibility(View.GONE);

webView 内存泄漏问题 以及 销毁

  1. 当webView加载大量的网络界面时,可能会产生大量的内存泄漏,

    • 展示webview的activity可以另开一个进程,

      Androidmanifest.xml 的activity标签里添加:

      Android:process=": webBrowsing"
      
      

      当结束这个进程时, 手动调用System.exit(0);

    • 可以在webView destroy 中调用下面的方法,在很大程度上可避免内存泄漏:

      public void releaseAllWebViewCallback() {
       if (android.os.Build.VERSION.SDK_INT < 16) {
           try {
               Field field = WebView.class.getDeclaredField("mWebViewCore");
               field = field.getType().getDeclaredField("mBrowserFrame");
               field = field.getType().getDeclaredField("sConfigCallback");
               field.setAccessible(true);
               field.set(null, null);
           } catch (NoSuchFieldException e) {
               if (BuildConfig.DEBUG) {
                   e.printStackTrace();
               }
           } catch (IllegalAccessException e) {
               if (BuildConfig.DEBUG) {
                   e.printStackTrace();
               }
           }
       } else {
           try {
               Field sConfigCallback = Class.forName("android.webkit.BrowserFrame").getDeclaredField("sConfigCallback");
               if (sConfigCallback != null) {
                   sConfigCallback.setAccessible(true);
                   sConfigCallback.set(null, null);
               }
           } catch (NoSuchFieldException e) {
               if (BuildConfig.DEBUG) {
                   e.printStackTrace();
               }
           } catch (ClassNotFoundException e) {
               if (BuildConfig.DEBUG) {
                   e.printStackTrace();
               }
           } catch (IllegalAccessException e) {
               if (BuildConfig.DEBUG) {
                   e.printStackTrace();
               }
           }
       }
      

    }
    ```

  2. WebView 的销毁:

    一定要进行 WebView 的销毁!!!

    在activity 的onDestroy 中去调用:ProgressWebView 的onDestroy();

    在 ProgressWebView 中:

     public void onDestroy() {
        Log.e(TAG, "WebView onDestroy");
    
        if (webView != null) {
            // 要首先移除webview
            removeView(webView);
    
            // 清理缓存
            webView.stopLoading();
            webView.onPause();
            webView.clearHistory();
            webView.clearCache(true);
            webView.clearFormData();
            webView.clearSslPreferences();
            WebStorage.getInstance().deleteAllData();
            webView.destroyDrawingCache();
            webView.removeAllViews();
            
            // 最后再去webView.destroy();
            webView.destroy();
        }
    
        // 清理cookie 
        CookieSyncManager.createInstance(HSApplication.getContext());
        CookieSyncManager.getInstance().startSync();
        CookieManager.getInstance().removeSessionCookie();
    
    }
    

    步骤:

    1. 首先要removeView;
    2. 清理缓存;
    3. webView.removeAllViews();
    4. 最后 webView.destroy();
  1. webSetting.setBuiltInZoomControls(true) 引发的crash;

    这个方法调用以后 如果触摸屏幕 弹出的提示框还没消失的时候 如果activity结束了 就会报错了。3.0以上 4.4以下很多手机会出现这种情况。

    解决:

    在activity 的onDestroy() 中把 webview设置为 setVisibility(View.GONE);

  1. 加载视频的问题:

    要想实现webview 全屏加载视频,需要在webView.setWebChromeClient()里重写onShowCustomView()onHideCustomView(),这两个方法:

    //onShowCustomView()方法里面的实现:
    //customView 为 一个新的全屏的view
    // 全屏
     activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    
    //
    if(customView != null) {
        callBack.onCustomViewHidden();
        return;
    }
    
    customView = view;
    customViewCallback = callback;
    videoShowLayout.addView(customView);
    videoShowLayout.setVisibility(View.VISIBLE);
    //videoShowLayout 是视频全屏的加载的父布局
    webDetailLayout.setVisibility(View.INVISIBLE);
    
    

    onHideCustomView()的实现如下:

    if (customView == null) {
        return;
    }
    //界面转回垂直
    activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    webDetailLayout.setVisibility(View.VISIBLE);
    
    customView.setVisibility(View.GONE);
    videoShowLayout.removeView(customView);
    customView = null;
    videoShowLayout.setVisibility(View.GONE);
    customViewCallback.onCustomViewHidden();
    
    

注意: 注意根布局的背景,因为在全屏的切换中可能会出现一些白色、黑色的底色,一般是由根布局的背景色引起的。

注: 以上是初稿,可能会有错误,如发现错误,请不吝留言,谢谢;

后面,会继续更新有关 WebView 的内容

参考链接:

  1. 很详细的一篇介绍

  2. WebView 遇到的问题

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,397评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • 这篇博客主要来介绍 WebView 的相关使用方法,常见的几个漏洞,开发中可能遇到的坑和最后解决相应漏洞的源码,以...
    Shawn_Dut阅读 7,201评论 3 55
  • WebView·开车指南 2016-08-31BugDev 北京市东城区首席Bug布道师开山之作,一整月交通事故血...
    53c021c38a1d阅读 825评论 0 1
  • 今天晚上摆的不是地摊,是闹剧。 刚摆好东西没多久,一位阿姨就过来让我给她挑一条围巾,然后也没试戴就开始掏钱给我。她...
    柒壹阅读 281评论 0 2