美文网首页android
WebView你可能不知道的细节

WebView你可能不知道的细节

作者: ChicoChen | 来源:发表于2017-05-09 17:05 被阅读590次

    前言

    现在Android应用native和H5结合的越来越紧密。最近也一直在处理Android原生页面和H5交互的问题,发现好多以前都没有注意过的细节,统一整理下,希望对自己,对大家有所帮助。

    WebSettings设置

    //设置是否支持缩放,我这里为false,默认为true。
    mWebView.getSettings().setSupportZoom(false);
    
    //设置是否显示缩放工具,默认为false
    mWebView.getSettings().setBuiltInZoomControls(false);
    
    //设置默认的字体大小,默认为16,有效值区间在1-72之间
    mWebView.getSettings().setDefaultFontSize(18);
    
    //首先阻塞图片,让图片不显示
    mWebSettings.setBlockNetworkImage(true);
    
    //页面加载好以后,在放开图片
    mWebSettings.setBlockNetworkImage(false);
    
    //设置自适应屏幕
    webSettings.setUseWideViewPort(true); 
    // 缩放至屏幕的大小
    webSettings.setLoadWithOverviewMode(true); 
    
    //支持插件
    webSettings.setPluginsEnabled(true); 
    
    //提高渲染等级
    mWebSettings.setRenderPriority(WebSettings.RenderPriority.HIGH);
    
    //禁止webview上面控件获取焦点(黄色边框)
    mWebSettings.setNeedInitialFocus(false);
    
    //关闭webview中缓存 
    webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); 
    
    //设置可以访问文件 
    webSettings.setAllowFileAccess(true); 
    
    //支持通过JS打开新窗口 
    webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
    
    //支持自动加载图片 
    webSettings.setLoadsImagesAutomatically(true); 
    
    //设置编码格式
    webSettings.setDefaultTextEncodingName("utf-8");
    
    

    webview加载方式

    加载asset中html文件

    webview.loadUrl("file:///android_asset/index.html");
    

    加载网络url

    webview.loadUrl("http://www.baidu.com");
    

    加载手机本地html文件

    webView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");
    

    加载带参数的post接口

    String postDate = "name=zhangsan&age=18&sex=1";
    webView.postUrl("www.test.com",EncodingUtils.getBytes(postDate,"BASE64"));
    

    加载html字符串

    //不支持#、%、\、? 四种字符
    webView.loadData(URLEncoder.encode(data, encoding),"text/html", "utf-8");  
    
    webView.loadDataWithBaseURL("about:blank", html, "text/html", "utf-8", null);
    

    Back键控制网页后退

    public boolean onKeyDown(int keyCode, KeyEvent event) {
      if ((keyCode == KEYCODE_BACK) && mWebView.canGoBack()) { 
          mWebView.goBack();
          return true;
      }
      return super.onKeyDown(keyCode, event);
    }
    

    是否调用外部浏览器

    webview.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }
    });
    

    当设置WebViewClient时,不会跳转到外部浏览器,否则会跳转到外部浏览器

    Webview内存泄漏问题

    通常我们声明一个webview是使用xml布局实现的

    <WebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    

    然而使用这种方式,加载webview的Activity得不到销毁,即使使用以下代码也没用任何效果

    @Override
    protected void onDestroy() {
        super.onDestroy();
        webview.destroy();
        webview=null;
    }
    

    为了验证加载webview的Activity一直被持有,可以通过Android Studio来查看
    点击Android Monitor,然后选择Monitors,点击下图圆圈部分


    image

    然后会生成一个.hprof文件,进入到Activity目录下面


    image
    你会发现没进入一次,WenviewActivity的total cout和heap cout会增加一次。

    解决使用webview造成的内存泄漏问题,我们通常不使用xml布局文件声明webview,而是动态生成webview。

    首先是在xml文件中的webview位置生明一个LinearLayout

    <LinearLayout
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    

    然后只onCreate方法动态生明一个webview,把webview添加到LinearLayout中

    webView=new WebView(getApplicationgContext());
    linearLayout.addView(webview);
    

    最后中onDestroy()移除LinearLayout所有子试图

     @Override
    protected void onDestroy() {
        super.onDestroy();
        webView.removeAllViews()
        webview.destroy();
        webview=null;
    }
    

    javascript互调

    原生与H5互相调用情况之一是javascript互调。

    不管是H5调用原生还是原生调用H5,首先我们都需要设置webview支持javascript,所以我们必须加上下面这行代码

    WebView.getSettings().setJavaScriptEnabled(true);
    

    有了javascript支持,你就可以尽情的去相互调用了。

    我们通过一个实际示例来分析一下

    有这么一个需求,Html页面上长按语音录制按钮调用手机原生语音录制功能,然后对语音进行识别,把识别后的String传递个Html。在这个需求中,就涉及到Html调用原生语音录制以及原生调用Htmljavascript方法将识别后的语音通过字符串传给Html。

    首先我们实现长按Html按钮调用本地语音录制,这就是Html调用原生方法了,这里的方法是本地创建的js方法

    private class AshJavaScriptInterface{
        public AshJavaScriptInterface(BaseActivity context) {
            super(context);
        }
        @android.webkit.JavascriptInterface
        public void onTouchDown() {
            //TODO 按钮被按下,类似于原生按下操作
        }
    
        @android.webkit.JavascriptInterface
        public void onTouchUp() {
            //TODO 按钮被松开,类似于原生松开操作
        }
    }
    

    将创建的Js方法设置到webview中

    public AshJavaScriptInterface createJsInterface() {
        return new AshJavaScriptInterface(this);
    }
    
    webView.addJavascriptInterface(jsExec,GlobalVariable.JS_INTERFACE_OBJ;
    

    然后剩余的工作就是Html前端的工作了,前端监听按钮事件,当按钮被按下时调用onTouchDown方法(比如QBaoJSBridge.onTouchDown()),而客户端就在onTouchDown方法中处理按钮被按下后的操作,当手松开之后,前端调用onTouchUp方法(比如QBaoJSBridge.onTouchUp()),客户端同时在onTouchUp方法中处理手被松开的操作。到这里,按钮长按监听就完成了,这里主要是Html调用原声Js方法。

    语音录制之后,客户端需要对录制的语音进行识别,将语音转换成String字符串(我们这里是使用百度语音识别),识别过程是一个异步耗时的操作,当我们完成识别之后需要把字符串传递给Html,那么我们就需要调用Html的js方法了

    webView.loadUrl("javascript:sendMsgByApp(\"" + msg + "\")");
    

    sendMsgByApp方法就是前端定义的一个Js方法,做完这一步,这个需求整个流程就结束了,整个过程中实现了Html和原生的js相互调用

    除了以上场景,比如Html要获取手机设备号,点击Html按钮弹出对话框等都可以通过调用本地方法实现

    html链接处理

    我们常常会遇到点击Html按钮跳转到原生指定的Activity页面或者获取url链接中的参数进行本地接口调用等情况

    那么我们就需要对url链接进行处理,通过判断url进行不同需求操作

    具体如何实现的呢?

    首先要设置webview的WebViewClient,当我们点击html按钮或者链接时会调用shouldOverrideUrlLoading方法,所以我们对点击进行操作时通常也是在shouldOverrideUrlLoading方法中进行处理

     webview.setWebViewClient(new WebViewClient(){
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request){
            boolean isHandle = handleProtocol(url);
            if (isHandle){
            return true;
        }
            return super.shouldOverrideUrlLoading(view, request);
        }
    });
    

    handleProtocol就是url处理方法

    protected boolean handleQianBaoProtocol(String url) {
        Uri uri = Uri.parse(url);
        String scheme = uri.getScheme();
        String host = uri.getHost();
        String path = uri.getPath();
        
        if ("https".equals(uri.getScheme())
                    && "maliprod.alipay.com".equals(uri.getHost())
                    && "/w/trade_pay.do".equals(uri.getPath())) {
            String orderIds = uri.getQueryParameter("pay_order_id");
            if (!TextUtils.isEmpty(orderIds)) {
                //提交商品订单
            }
        }
        
         if ("newtab".equals(uri.getScheme())
                && "goodstuff.qbao.com".equals(uri.getHost())
                && "/similar".equals(uri.getPath())) {
            String pid = uri.getQueryParameter("pid");
            FindLikeActivity.startActivity(this, pid);
            return true;
        }
    return false;
    }
    

    webview长按监听

    长按主要是针对图片、链接、电话、email等等,所以监听长按,我们可以进行图片打开、调用手机邮箱、打电话、分享等操作

    监听代码如下

    webview.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            WebView.HitTestResult result = ((WebView) v).getHitTestResult();
            if (null == result) return true;
            int type = result.getType();
            if (type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE ||
                    type == WebView.HitTestResult.IMAGE_TYPE) {
                String imgUrl = result.getExtra();
                if (!TextUtils.isEmpty(imgUrl)) {
                    onImgLongClick(imgUrl);
                    return true;
                }
            }
            return true;
        }
    });
    

    以上代码主要长按打开图片,其他的具体操作可以通过查看HitTestResult类,里面有不同类型的常量,通过判断type来进行我们具体的操作

    设置html加载进度

    加载网络url一般都是耗时操作,而且当网络环境比较差的时候,加载花费的时间可能更长,为了比较好的体验,我们通常会给一个加载进度。

    
    class MyWebChrimeClien extends WebChromeClient {
        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            if (newProgress == 100) {
                mProgress.setVisibility(View.GONE);
            } else {
                if (View.INVISIBLE == mProgress.getVisibility()) {
                    mProgress.setVisibility(View.VISIBLE);
                }
                mProgress.setProgress(newProgress);
            }
            super.onProgressChanged(view, newProgress);
        }
    }
    
    mWebView.setWebChromeClient(new MyWebChrimeClien());
    

    主要是通过设置WebChromeClient,通过newProgress的数字判断加载到什么位置,这个进度是包含百分比的,如果不需要知道具体百分比,可以通过设置WebViewClient,
    重写onPageStarted方法和onPageFinished方法

    class MyWebViewClient extends WebViewClient {
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }
    
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
            mProgress.setVisibility(View.VISIBLE);
        }
    
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            mProgress.setVisibility(View.GONE);
        }
    }
    
    mWebView.setWebViewClient(new MyWebViewClient());
    
    

    具体使用哪种方式,那就看你实际需求了,而且进度条也可以根据自己的需求进行自定义

    相关文章

      网友评论

      • 9703a1c0d8bc:动态生明一个webview,把webview添加到LinearLayout中,有时候会加载不出网页

      本文标题:WebView你可能不知道的细节

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