美文网首页
WebView整理

WebView整理

作者: 30cf443c3643 | 来源:发表于2018-09-06 11:56 被阅读30次

Android的Webview在低版本和高版本采用了不同的webkit版本内核,4.4后直接使用了Chrome。

加载HTML的四种方法

webView.loadUrl("http://139.196.35.30:8080/OkHttpTest/apppackage/test.html");//加载url

webView.loadUrl("file:///android_asset/test.html");//加载asset文件夹下html

//方式3:加载手机sdcard上的html页面
webView.loadUrl("content://com.ansen.webview/sdcard/test.html");

//方式4 使用webview显示html代码
webView.loadDataWithBaseURL(null,"<html><head><title> 欢迎您 </title></head>" +
        "<body><h2>使用webview显示 html代码</h2></body></html>", "text/html" , "utf-8", null);

WebSettings

private void initWebSetting(){
        WebSettings webSettings = mWeb .getSettings();
        mWeb.requestFocusFromTouch();//支持获取手势焦点,输入用户名、密码或其他
        webSettings.setJavaScriptEnabled(true);
        webSettings.setRenderPriority(WebSettings.RenderPriority.HIGH);//提高渲染的优先级

        //设置自适应屏幕,两者合用
        webSettings.setUseWideViewPort(true);//将图片调整到适合webview的大小
        webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
        webSettings.setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。
        webSettings.setBuiltInZoomControls(true);//设置内置的缩放控件。
        //若上面是false,则该WebView不可缩放,这个不管设置什么都不能缩放。
        webSettings.setTextZoom(2);//设置文本的缩放倍数,默认为 100
        webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件
        webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); //支持内容重新布局,调用该方法会引起页面重绘。
        webSettings.supportMultipleWindows(); //多窗口
        webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);  //关闭webview中缓存
        webSettings.setAllowFileAccess(true);
        webSettings.setNeedInitialFocus(true);//当webview调用requestFocus时为webview设置节点
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
        webSettings.setLoadsImagesAutomatically(true);//支持自动加载图片
        webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式
        webSettings.setStandardFontFamily(""); //设置 WebView 的字体,默认字体为 "sans-serif"
        webSettings.setDefaultFontSize(20);//设置 WebView 字体的大小,默认大小为 16
        webSettings.setMinimumFontSize(12);//设置 WebView 支持的最小字体大小,默认为 8

        webSettings.setBlockNetworkImage(true);//禁止WebView从网络上加载图片。默认值false允许。
        webSettings.setUserAgentString("Android");//设置WebView的UserAgent值。
    }

关于缓存CacheMode

  • LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
  • LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
  • LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
  • LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
    结合使用(离线加载):
if (NetStatusUtil.isConnected(getApplicationContext())) {
    webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);//根据cache-control决定是否从网络上取数据。
} else {
    webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//没网,则从本地获取,即离线加载
}

webSettings.setDomStorageEnabled(true); // 开启 DOM storage API 功能
webSettings.setDatabaseEnabled(true);   //开启 database storage API 功能
webSettings.setAppCacheEnabled(true);//开启 Application Caches 功能

String cacheDirPath = getFilesDir().getAbsolutePath() + APP_CACAHE_DIRNAME;
webSettings.setAppCachePath(cacheDirPath); //设置  Application Caches 缓存目录
//每个 Application 只调用一次 WebSettings.setAppCachePath(),WebSettings.setAppCacheMaxSize()

添加 HTTP 请求头(Header)

loadUrl(String url, Map<String, String> additionalHttpHeaders)

WebViewClient

WebViewClient就是帮助WebView处理各种通知、请求事件的。
打开网页时不调用系统浏览器, 而是在本WebView中显示:

mWeb.setWebViewClient(new WebViewClient(){
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return false;
               //True if the host application wants to leave the current webview and handle the url itself, otherwise return false.
            }

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

            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                super.onPageStarted(view, url, favicon); 
            //这个事件就是开始载入页面调用的,我们可以设定一个loading的页面,告诉用户程序在等待网络响应。
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
              //在页面加载结束时调用。同样道理,我们可以关闭loading 条,切换程序动作。
            }

            @Override
            public void onLoadResource(WebView view, String url) {
                super.onLoadResource(view, url);
          // 在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。
            }

            @Override
            public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
                super.onReceivedError(view, request, error);// (报告错误信息)
            }

            @Override
            public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
                super.doUpdateVisitedHistory(view, url, isReload);  //(更新历史记录)
            }

            @Override
            public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
                return super.shouldOverrideKeyEvent(view, event);
              //重写此方法才能够处理在浏览器中的按键事件。
            }

            @Override
            public void onFormResubmission(WebView view, Message dontResend, Message resend) {
                super.onFormResubmission(view, dontResend, resend); //(应用程序重新请求网页数据)
            }

            @Override
            public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
                super.onReceivedHttpAuthRequest(view, handler, host, realm); //(获取返回信息授权请求)
            }

@Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                super.onReceivedSslError(view, handler, error);//重写此方法可以让webview处理https请求。
            }

            @Override
            public void onScaleChanged(WebView view, float oldScale, float newScale) {
                super.onScaleChanged(view, oldScale, newScale); // (WebView发生改变时调用)
            }

            @Override
            public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
                super.onUnhandledKeyEvent(view, event);//(Key事件未被加载时调用)
            }

           @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
                return super.shouldInterceptRequest(view, request); 
                // 拦截替换网络请求数据,  从API 21开始引入
            }
        });

WebChromeClient

WebChromeClient是辅助WebView处理Javascript的对话框,网站图标,网站title,加载进度等

WebChromeClient Chromeclient = new WebChromeClient(){
        @Override
        public void onProgressChanged(WebView view, int newProgress) {
                  //newProgress 0-100整数
        }

        @Override
        public void onReceivedTitle(WebView view, String title) {
            MainActivity.this.setTitle(title);
        }

        @Override
        public void onReceivedIcon(WebView view, Bitmap icon) {
            //
        }

        @Override
        public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
            //
            return true;
        }

        @Override
        public void onCloseWindow(WebView window) {
        }

        //不支持js的alert弹窗,需要自己监听然后通过dialog弹窗
        @Override
        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
            AlertDialog.Builder localBuilder = new AlertDialog.Builder(webView.getContext());
            localBuilder.setMessage(message).setPositiveButton("确定",null);
            localBuilder.setCancelable(false);
            localBuilder.create().show();

            //注意:
            //必须要这一句代码:result.confirm()表示:
            //处理结果为确定状态同时唤醒WebCore线程
            //否则不能继续点击按钮
            result.confirm();
            return true;
        }

        //处理confirm弹出框
        @Override
        public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult
                result) {
            //
            return true;
        }

        //处理prompt弹出框
        @Override
        public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
            //
            return true;
        }

JS和android 互调

对于Android调用JS代码的方法有2种:

  1. 通过WebView的loadUrl()
  2. 通过WebView的evaluateJavascript()

对于JS调用Android代码的方法有3种:

  1. 通过WebView的addJavascriptInterface()进行对象映射 被JS调用的方法必须加入@JavascriptInterface注解
  2. 通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url
  3. 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息

参考交互方式

实现webview调用系统文件选择器

原来5.0的WebChromeClient不是用的openFileChooser(…),而是用的onShowFileChooser(…)



    private ValueCallback<Uri[]> mUploadCallbackAboveFive;
    private Activity mActivity;
    private ValueCallback<Uri> mUploadMessage;

    // For Android < 3.0
    public void openFileChooser(ValueCallback<Uri> uploadMsg) {
        openFileChooser(mUploadMessage, "", "");
    }

    // For Android 3.0+
    public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
        openFileChooser(mUploadMessage, acceptType, "");
    }

    //For Android 4.1
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        mActivity.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
    }

    /**
     * 兼容5.0及以上
     *
     * @param webView
     * @param valueCallback
     * @param fileChooserParams
     * @return
     */
    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> valueCallback, android.webkit.WebChromeClient.FileChooserParams fileChooserParams) {
        mUploadCallbackAboveFive = valueCallback;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("*/*");
        mActivity.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
        return true;
   }


    @Override
    public void onActivityResultInternal(int resultCode, Intent data) {
           if (getPhoneAndroidSDK() < Build.VERSION_CODES.LOLLIPOP) {//5.0之前拿到照片的方式不相同
                if (fromTakePhoto && requestCode == 1 && resultCode == -1) {
                    mWeb.loadUrl("javascript:wave2('" + fileFullName + "')");
                } else {
                    mWeb.loadUrl("javascript:wave2('Please take your photo')");
                }
                fromTakePhoto = false;

                if (requestCode == FILECHOOSER_RESULTCODE) {
                    if (null == mUploadMessage)
                        return;
                    Uri result = data == null || resultCode != RESULT_OK ? null : data
                            .getData();
                    mUploadMessage.onReceiveValue(result);
                    mUploadMessage = null;
                }
            } else {
                if (requestCode != FILECHOOSER_RESULTCODE
                        || mUploadCallbackAboveL == null) {
                    return;
                }

                Uri[] results = null;
                if (resultCode == Activity.RESULT_OK) {
                    if (data == null) {

                    } else {
                        String dataString = data.getDataString();
                        ClipData clipData = data.getClipData();

                        if (clipData != null) {
                            results = new Uri[clipData.getItemCount()];
                            for (int i = 0; i < clipData.getItemCount(); i++) {
                                ClipData.Item item = clipData.getItemAt(i);
                                results[i] = item.getUri();
                            }
                        }

                        if (dataString != null)
                            results = new Uri[]{Uri.parse(dataString)};
                    }
                }
                mUploadCallbackAboveL.onReceiveValue(results);
                mUploadCallbackAboveL = null;
                return;
           }
        }

在 WebView 中长按保存图片

首先设置监听

mWebview.setOnLongClickListener(new View.OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {

    }
});

获取类型

WebView.HitTestResult result = ((WebView) v).getHitTestResult();
int type = result.getType();           //图片类型
String imgurl = result.getExtra();   //图片地址
WebView.HitTestResult.UNKNOWN_TYPE 未知类型
WebView.HitTestResult.PHONE_TYPE 电话类型
WebView.HitTestResult.EMAIL_TYPE 电子邮件类型
WebView.HitTestResult.GEO_TYPE 地图类型
WebView.HitTestResult.SRC_ANCHOR_TYPE 超链接类型
WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE 带有链接的图片类型
WebView.HitTestResult.IMAGE_TYPE 单纯的图片类型
WebView.HitTestResult.EDIT_TEXT_TYPE 选中的文字类型

最后操作图片

mWebView.setOnLongClickListener(new View.OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
        WebView.HitTestResult result = ((WebView)v).getHitTestResult();
        if (null == result)
            return false;
        int type = result.getType();
        if (type == WebView.HitTestResult.UNKNOWN_TYPE)
            return false;

        // 这里可以拦截很多类型,我们只处理图片类型就可以了
        switch (type) {
            case WebView.HitTestResult.PHONE_TYPE: // 处理拨号
                break;
            case WebView.HitTestResult.EMAIL_TYPE: // 处理Email
                break;
            case WebView.HitTestResult.GEO_TYPE: // 地图类型
                break;
            case WebView.HitTestResult.SRC_ANCHOR_TYPE: // 超链接
                break;
            case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
                break;
            case WebView.HitTestResult.IMAGE_TYPE: // 处理长按图片的菜单项
                // 获取图片的路径
                String saveImgUrl = result.getExtra();

                // 跳转到图片详情页,显示图片
                Intent i = new Intent(MainActivity.this, ImageActivity.class);
                i.putExtra("imgUrl", saveImgUrl);
                startActivity(i);
                break;
            default:
                break;
        }
    }
});

Android5.0 WebView中Http和Https混合问题

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
     webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}

参数类型说明:
MIXED_CONTENT_ALWAYS_ALLOW:允许从任何来源加载内容,即使起源是不安全的;
MIXED_CONTENT_NEVER_ALLOW:不允许Https加载Http的内容,即不允许从安全的起源去加载一个不安全的资源;
MIXED_CONTENT_COMPATIBILITY_MODE:当涉及到混合式内容时,WebView 会尝试去兼容最新Web浏览器的风格。

在5.0以下 Android 默认是 全允许,
但是到了5.0以上,就是不允许,实际情况下很我们很难确定所有的网页都是https的,所以就需要这一步的操作。

避免WebView内存泄露的方式

1不再xml中定义WebView,而是用代码动态添加

    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    mWebView = new WebView(getApplicationContext());
    mWebView.setLayoutParams(params);
    mLayout.addView(mWebView);

2.在 Activity 销毁的时候,可以先让 WebView 加载null内容,然后移除 WebView,再销毁 WebView,最后置空。

@Override
 protected void onDestroy() {
      if (mWebView != null) {
          mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
          mWebView.clearHistory();

         ((ViewGroup) mWebView.getParent()).removeView(mWebView);
          mWebView.destroy();            
          mWebView = null;
        }
        super.onDestroy();
    }

WebView的方法

goBack()//后退
goForward()//前进
goBackOrForward(intsteps) //以当前的index为起始点前进或者后退到历史记录中指定的steps,
                              如果steps为负数则为后退,正数则为前进

canGoForward()//是否可以前进
canGoBack() //是否可以后退

清除数据:
clearCache(true);//清除网页访问留下的缓存,由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.
clearHistory()//清除当前webview访问的历史记录,只会webview访问历史记录里的所有记录除了当前访问记录.
clearFormData()//这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据。

WebView状态:
onResume() //激活WebView为活跃状态,能正常执行网页的响应
onPause()//当页面被失去焦点被切换到后台不可见状态,需要执行onPause动过, onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。

pauseTimers()//当应用程序被切换到后台我们使用了webview, 这个方法不仅仅针对当前的webview而是全局的全应用程序的webview,它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。
resumeTimers()//恢复pauseTimers时的动作。

destroy()//销毁,关闭了Activity时,音乐或视频,还在播放。就必须销毁。


判断WebView是否已经滚动到页面底端 或者 顶端:
因为webview可以缩放
if (webView.getContentHeight() * webView.getScale() == (webView.getHeight() + webView.getScrollY())) {
        //已经处于底端
    }

if(webView.getScrollY() == 0){
        //处于顶端
    }

考拉团队的如何设计健壮的webview

漏洞

阿里巴巴的开发手册中强制要求

// 禁用 file 协议;
setAllowFileAccess(false); 
setAllowFileAccessFromFileURLs(false);
setAllowUniversalAccessFromFileURLs(false);

会造成隐私的泄漏
你不知道的 Android WebView 使用漏洞

相关文章

  • WebView整理

    Android的Webview在低版本和高版本采用了不同的webkit版本内核,4.4后直接使用了Chrome。 ...

  • Android WebView 学习

    第一部分:常用方法整理 WebView常用方法 WebView周边相关类 WebView缓存机制类型 Androi...

  • Webview 整理笔记

    初始化操作及常见用法 WebSettings、WebViewClient、WebChromeClient 的常用方...

  • WebView的整理

    一,webView的常见的一些坑; 二,关于webView的内存泄露问题; ...

  • UIWebView加载时添加请求头(含NSURLProtocol

    前言 最近app需要重新整理webview加载的架构,把webview的请求方式由post改为get,而之前加载时...

  • WebView基础

    之前webView的使用很大多局限于显示网页,现在有机会重头梳理下整理成如下笔记: 基本用法 WebView 的状...

  • android webview 梳理

    webview 零零散散的点也是有一些的,今天整理一下,首先我们一般不直接使用 webview 本身,而是对给 w...

  • 整理:webView与js交互

  • WKWebview里JS交互的问题

    关于webview的与JS交互这里比较全,感谢作者的整理。https://github.com/Haley-Won...

  • WebView·开车指南

    WebView·开车指南 目录 WebView简介 WebView基本使用 WebView常用方法 WebSett...

网友评论

      本文标题:WebView整理

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