WebView使用全攻略

1.> 转载请标明出处

本文出自[HCY的微博]

一、概述

移动web开发相对原生开发有以下好处:

  • 开发成本低,可以适配多种平台的设备
  • 迭代更新成本低,可以快速的实现更新的内容全覆盖

所以对于频繁更新的业务,比如商城。就比较适合用web进行开发。采用web开发,在App端必然离不开WebView这个组件。本文将从以下几个方面阐述WebView的相关知识及Web开发中的常用技巧。

  • 内容加载
  • 与服务端的交互
  • 性能优化
  • 安全性
  • Web开发常用技巧

二、内容加载

WebView的内容加载主要分为以下三种方式。

  • 加载远端页面
  • 加载本地页面
  • 加载html代码

加载远端代码

比如加载百度

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

加载本地代码

比如加载assets/error.html

wvTest.loadUrl("file:///android_asset/error.html");

加载html代码

    String summary = "<html><body>You scored <b>192</b> points.</body></html>";
    wvTest.loadData(summary, "text/html", null);

三、与服务端的交互

WebView与服务端的交互方式主要有两种:

  • JavaScript的交互方式
  • 拦截Url请求的交互方式

JavaScript的交互方式

假设有一个名为error.html的网页文件在assets目录中,代码如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>页面无法显示</title>
<style type="text/css">
html,body,div,img,p,ul,li{ margin:0; padding:0;}
html,body,img {width:100%; height:100%;}
.wrapper{ position:relative; margin:0 auto;width:100%; height:100%; min-height:445px; overflow:hidden; font-size:14px; line-height:180%;}
.wrapper img { width:100%; height:100%;}
.wrapper p{ margin-left:8px;  padding-right:15px; position:absolute;top:50px;}
a.refresh{ float:right; -webkit-border-radius:5px;  background:#ccdeff; display:block; width:60px; height:20px; text-align:center; line-height:20px; font-size:12px; text-decoration:none; color:#201f1f; -webkit-tap-highlight-color:#a7c7ff;}
.wrapper ul{ margin-left:35px; margin-top:5px; position:absolute;top:100px;}
</style>
</head>

<body>
 <script language="javascript">
         //提供给App端调用测试的代码
         function testForClient() {
                   window.error.refresh();   

                }
        </script>
<div class="wrapper">
    //调用App端名为error的对象的refresh方法
    <p><a href="javascript:window.error.refresh()" class="refresh">刷新</a>该页面无法显示,您可以尝试:</p>
    <ul>
        <li>检查您的网络连接是否正常</li>
        <li>点击右侧刷新按钮重新连接</li>
        <li>可能是服务器故障,请稍后再试</li>
    </ul>
</div>
</body>
</html>

JavaScript调用App端的方法
1.开启JS功能,这一步很重要

wvTest.getSettings().setJavaScriptEnabled(true);

2.使用WebView的addJavascriptInterface(Object object, String name)方法将App端的对象注入到WebView中,其中object为注入到JS上下文中的对象,其中的name就是JS中使用的对象名
例如将要被使用的对象的类代码如下

public class JavascriptSimple {
    String url = null;
    WebView view = null;

    public JavascriptSimple(WebView view, String url) {
        this.view = view;
        this.url = url;
    }
    //1.为了防止JS反射攻击,4.1以上的系统必须添加此注解,否则将无法被JS调用
    //2.当然,只有public方法才能被调用
    //3.在JS中被调用的方法都在JavaBridge线程里面,而WebView所有的方法都只能在主线程中调用。
    @JavascriptInterface
    public void refresh() {
        if (view == null || TextUtils.isEmpty(url)) {
            return;
        }
        // 所有JS方法都在JavaBridge线程中回调,所以对于UI操作要回调到主线程
        Handler mainHandler = new Handler(Looper.getMainLooper());
        mainHandler.post(new Runnable() {

            @Override
            public void run() {
                view.loadUrl(url);
            }
        });
    }
}

注入到JS中的代码为

wvTest.addJavascriptInterface(new JavascriptSimple(wvTest, ""), "error");

3.在被调用的方法前面加上@JavascriptInterface注解,若不添加,4.1以上的系统中JS将无法成功调用App端的方法。

4.在JS中被调用的方法都在JavaBridge线程里面,而WebView所有的方法都只能在主线程中调用。。否则会出现警告信息A WebView method was called on thread ‘JavaBridge’. All WebView methods must be called on the same thread

APP调用JavaScript的方法

1.首先必须要加载JavaScript方法所在的Url,否则App端无法调用成功。

wvTest.loadUrl("file:///android_asset/error.html");

2.调用JavaScript方法

    btDemo.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                wvTest.loadUrl("javascript:testForClient()");
            }
        });

拦截Url请求的交互方式

1.首先要重写WebViewClient的shouldOverrideUrlLoading方法

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (TextUtils.isEmpty(url)) {
            return true;
        }
        //继续做URL解析,执行特定的业务逻辑处理
        return doOverrideUrlLoading(view, url);
    }

拦截请求响应实现离线缓存

        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
        //return null表示不拦截响应请求
            return cacheEnabled ? interceptRequest(request.getUrl().toString()) : null;
        }

private WebResourceResponse interceptRequest(String url) {
            if (TextUtils.isEmpty(url)) {
                return null;
            }
            byte[] binary = aCache.getAsBinary(url);
            if (binary == null || isUpdate) {
                byte[] bytes = cacheRequest(url);
                binary = bytes;
            }
            if (binary == null || !isUseCache) {
                return null;
            } else {
                LogUtils.i(String.format("read %s cache", url));
                InputStream inputStream = new ByteArrayInputStream(binary);
                return new WebResourceResponse("", "UTF-8", inputStream);
            }
        }

        private byte[] cacheRequest(String url) {
            try {
                OkHttpClient client = new OkHttpClient();
                Request request = new Request.Builder()
                        .url(url)
                        .build();
                Response response = client.newCall(request).execute();
                if (!response.isSuccessful()) {
                    log(String.format("cache %s failed", url));
                    return null;
                } else {
                    log(String.format("cache %s succeeded", url));
                    byte[] bytes = response.body().bytes();
                    aCache.put(url, bytes);
                    return bytes;
                }

            } catch (Exception e) {
                e.printStackTrace();
                log(String.format("cache %s failed", url));
                return null;
            }
        }

2.解析Url,做特定的业务逻辑处理。
比如有http://www.xxx.com/?act=test&p={此处为json数据}
就可以通过act来区分不同的业务,p后面的json数据可以作为参数,传递给业务处理方法。

四、WebView的性能优化

加快渲染速度

可以在页面开始加载时调用setBlockNetworkImage(true);方法来阻止图片的加载,先渲染除图片以外的东西。当加载完成之后调用setBlockNetworkImage(false);方法来开启图片的加载,经过测试加载速度快了许多。

public abstract class BaseWebViewClient extends WebViewClient {
    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {
        // 为了加快加载速度,先阻止图片加载,待数据加载完毕在onPageFinished回调中开启图片加载功能
        view.getSettings().setBlockNetworkImage(true);

    }

    @Override
    public void onPageFinished(WebView view, String url) {
        // 网页加载完毕,开启加载图片
        view.getSettings().setBlockNetworkImage(false);

    }

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (TextUtils.isEmpty(url)) {
            return true;
        }
        //继续做URL解析,执行特定的业务逻辑处理
        return doOverrideUrlLoading(view, url);
    }

    /**
     * 子类中继续处理URL拦截操作
     * 
     * @param view
     * @param url
     * @return
     */
    protected abstract boolean doOverrideUrlLoading(WebView view, String url);

}

防止内存泄漏

在界面退出时要及时调用WebView的相关释放方法,以免内存泄漏。

    @Override
    protected void onDestroy() {
        super.onDestroy();
        wvTest.stopLoading();
        wvTest.removeAllViews();
        wvTest.destroy();
    }

五、WebView的安全性

1.JavaScript反射攻击

JavaScript可以通过反射技术,调用注入对象的父类的public方法,执行一些不安全代码。

解决方案
1.在被调用的方法前面加上@JavascriptInterface注解,在4.1以上的系统只有加了此注解的方法才能被JavaScript调用。

2.通过shouldOverrideUrlLoading方法屏蔽拦截不属于自己的网站的url请求。

六、Web开发常用技巧

1.从网页中启动App端的Activity

启动App端的Activity,可以配置URL Scheme采用隐式意图的方式启动。
Scheme介绍见http://developer.android.com/guide/topics/manifest/data-element.html

1.配置Scheme

       <activity android:name="com.hcy.test.SchemeAty" >

            <!-- android:exported="false" 含有intent-filter的组件默认是导出的,即可以被其它应用调用,若加上了这句代码就变成不可导出,第三方应用就没有权限调用这个组件,不管你下面怎么配置都是无法调用的 -->
            <intent-filter>

                <!-- 指定向用户显示数据的动作 -->
                <action android:name="android.intent.action.VIEW" />
                <!-- 隐式意图必须要的category,若没有这个无法启动 -->
                <category android:name="android.intent.category.DEFAULT" />
                <!-- 指定该Activity能被浏览器安全调用 -->
                <category android:name="android.intent.category.BROWSABLE" />
                <!-- 1.若scheme相同则继续匹配第2点,若scheme不相同则不符合调用要求 -->
                <!-- 2.若host相同则继续第3点,若host不相同则不符合调用要求 -->
                <!-- 3.若port相同,则符合调用要求,若port不相同则不符合调用要求 -->
                <!-- 4.scheme、host、port都是大小写敏感的,注意大小写区别 -->
                <data
                    android:host="test"
                    android:port="8990"
                    android:scheme="hcy" />
            </intent-filter>
        </activity>

2.启动这个Activity

在其它应用中启动

    Intent intent = new Intent();
    intent.setData(Uri.parse("hcy://test:8990/fc?p1=12&p2=1"));
    startActivity(intent);

在html中启动

<a href="hcy://test:8990/fc?p1=12&p2=1">launch activity by scheme</a>

3.解析意图内容

Intent intent = getIntent();
        StringBuilder sb = new StringBuilder();
        if (intent != null) {
            // 获取整个Scheme串
            sb.append("dataString=" + intent.getDataString() + ",");
            // 获取scheme
            sb.append("scheme=" + intent.getScheme() + ",");
            Uri uri = intent.getData();
            if (uri != null) {
                // 获取host
                sb.append("host=" + uri.getHost() + ",");
                // 获取port
                sb.append("port=" + uri.getPort() + ",");
                // 获取path
                sb.append("path=" + uri.getPath() + ",");
                // 获取请求内容
                sb.append("queryString=" + uri.getQuery() + ",");
                // 获取请求参数值
                sb.append("p1=" + uri.getQueryParameter("p1"));
            }
        }

结果为:

dataString=hcy://test:8990/fc?p1=12&p2=1,scheme=hcy,host=test,port=8990,path=/fc,queryString=p1=12&p2=1,p1=12

2.让WebView支持Https网页链接

重写WebViewClient的onReceivedSslError方法

@Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        //当发生SSL错误时,继续信任SSL证书
        handler.proceed();
    }
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值