Android WebView

 

 Android WebView 

一、简介

二、基本使用

三、常用方法

三、与JS交互

四、开源库

注:参考文章


一、简介

WebView在Android平台上是一个特殊的View, 基于webkit引擎、展现web页面的控件,这个类可以被用来在你的app中仅仅显示一张在线的网页,还可以用来开发浏览器。WebView内部实现是采用渲染引擎来展示view的内容,提供网页前进后退,网页放大,缩小,搜索。Android的Webview在低版本和高版本采用了不同的webkit版本内核,4.4后直接使用了Chrome内核。

现在很多APP都内置了Web网页,比如说很多电商平台,淘宝、京东、聚划算等等。WebView比较灵活,不需要升级客户端,只需要修改网页代码即可。一些经常变化的页面可以用WebView这种方式去加载网页。例如中秋节跟国庆节打开的页面不一样,如果是用WebView显示的话,只修改修改html页面就行,而不需要升级客户端。

二、基本使用

1、添加网络权限

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

2、添加布局

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

3、基本代码运用

webView = (WebView) findViewById(R.id.wv_webview);
// webView.loadUrl("file:///android_asset/test.html");//加载asset文件夹下html
webView.loadUrl("http://139.196.35.30:8080/OkHttpTest/apppackage/test.html");//加载url
//使用webview显示html代码
//webView.loadDataWithBaseURL(null,"<html><head><title> 欢迎您 </title></head>" +
//"<body><h2>使用webview显示 html代码</h2></body></html>", "text/html" , "utf-8", null);

webView.addJavascriptInterface(this,"android");//添加js监听 这样html就能调用客户端
webView.setWebChromeClient(webChromeClient);//辅助WebView处理Javascript的对话框,网站图标,网站title,加载进度条
webView.setWebViewClient(webViewClient); //帮助WebView处理各种通知、请求事件
WebSettings webSettings=webView.getSettings();//对WebView进行配置和管理
webSettings.setJavaScriptEnabled(true);//允许使用js
/**
  * LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
  * LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
  * LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
  * LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
  */
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);//不使用缓存,只从网络获取数据.
//支持屏幕缩放
webSettings.setSupportZoom(true);
webSettings.setBuiltInZoomControls(true);

三、常用方法

1、WebView常用方法

wvWebview.loadUrl(String url);// 加载网络链接 url
wvWebview.canGoBack();// 判断 WebView 当前是否可以返回上一页
wvWebview.goBack();// 回退到上一页
wvWebview.onResume();// 在调用onPause()后,可以调用该方法来恢复WebView 的运行
wvWebview.resumeTimers();// 恢复pauseTimers时的所有操作
wvWebview.onPause();// 类似 Activity 生命周期,页面进入后台不可见状态
wvWebview.pauseTimers();// 该方法面向全局整个应用程序的webview,它会暂停所有webview的layout,parsing,JavaScript Timer。当程序进入后台时,该方法的调用可以降低CPU功耗。
wvWebview.destroy();// 销毁 WebView
wvWebview.clearHistory();// 清空历史
wvWebview.clearCache(true);// 包括硬盘
wvWebview.reload();// 重新加载当前请求
wvWebview.removeAllViews();// 清除子view。
wvWebview.clearSslPreferences();// 清除ssl信息。
wvWebview.clearMatches();// 清除网页查找的高亮匹配字符。
wvWebview.removeJavascriptInterface(String interfaceName);// 删除interfaceName对应的注入对象
wvWebview.addJavascriptInterface(Object object,String interfaceName); //注入 java 对象。
wvWebview.setVerticalScrollBarEnabled(true);// 设置垂直方向滚动条。
wvWebview.setHorizontalScrollBarEnabled(true);// 设置横向滚动条。
wvWebview.loadUrl(String url, Map<String, String> additionalHttpHeaders);// 加载制定url并携带http header数据。。
wvWebview.stopLoading();// 停止 WebView 当前加载。
wvWebview.freeMemory();// 释放内存,不过貌似不好用。
wvWebview.clearFormData();// 清除自动完成填充的表单数据。需要注意的是,该方法仅仅清除当前表单域自动完成填充的表单数据,并不会清除WebView存储到本地的数据。

2、WebSettings(对WebView进行配置和管理) 常用方法

final String filesDir = getContext().getFilesDir().getPath();
final String databaseDir = filesDir.substring(0, filesDir.lastIndexOf("/")) + DATABASES_SUB_FOLDER;
WebSettings webSettings = wvWebview.getSettings();
if (webSettings == null) return;
webSettings.setJavaScriptEnabled(true);// 支持 Js 使用
webSettings.setDomStorageEnabled(true);// 开启DOM缓存
webSettings.setDatabaseEnabled(true);// 开启数据库缓存
webSettings.setLoadsImagesAutomatically(true);// 支持自动加载图片
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);// 设置 WebView 的缓存模式
webSettings.setAppCacheEnabled(true);// 支持启用缓存模式
// Android 私有缓存存储,如果你不调用setAppCachePath方法,WebView将不会产生这个目录
webSettings.setAppCachePath(getCacheDir().getAbsolutePath());
if (Build.VERSION.SDK_INT < 19) { // 数据库路径
    webSettings.setDatabasePath(databaseDir);
}
webSettings.setSupportZoom(true);// 支持缩放
webSettings.setUserAgentString("");// 设置 UserAgent 属性
webSettings.setAllowFileAccess(true);// 允许加载本地 html 文件/false
// 允许通过 file url 加载的 Javascript 读取其他的本地文件,Android 4.1 之前默认是true,在 Android 4.1 及以后默认是false,也就是禁止
webSettings.setAllowFileAccessFromFileURLs(false);
// 允许通过 file url 加载的 Javascript 可以访问其他的源,包括其他的文件和 http,https 等其他的源,
// Android 4.1 之前默认是true,在 Android 4.1 及以后默认是false,也就是禁止
// 如果此设置是允许,则 setAllowFileAccessFromFileURLs 不起做用
webSettings.setAllowUniversalAccessFromFileURLs(false);
webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式

3、WebChromeClient(辅助WebView处理Javascript的对话框,网站图标,网站title,加载进度条)常用方法

wvWebview.setWebChromeClient(new MyWebChromeClient());

public class MyWebChromeClient extends WebChromeClient {
        /**
         * 输出 Web 端日志
         */
        @Override
        public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
            return super.onConsoleMessage(consoleMessage);
        }
        /**
         * 当前 WebView 加载网页进度
         */
        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            super.onProgressChanged(view, newProgress);
        }
        /**
         * Js 中调用 alert() 函数,产生的对话框
         */
        @Override
        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
            return super.onJsAlert(view, url, message, result);
        }

        /**
         * 处理 Js 中的 Confirm 对话框
         */
        @Override
        public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
            return super.onJsConfirm(view, url, message, result);
        }

        /**
         * 处理 JS 中的 Prompt对话框
         */
        @Override
        public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
            return super.onJsPrompt(view, url, message, defaultValue, result);
        }

        /**
         * 接收web页面的icon
         */
        @Override
        public void onReceivedIcon(WebView view, Bitmap icon) {
            super.onReceivedIcon(view, icon);
        }

        /**
         * 接收web页面的 Title
         */
        @Override
        public void onReceivedTitle(WebView view, String title) {
            super.onReceivedTitle(view, title);
        }

    }

4、WebViewClient(帮助WebView处理各种通知、请求事件)常用方法

wvWebview.setWebViewClient(new MyWebViewClient());

public class MyWebViewClient extends WebViewClient {
        /**
         * 当WebView得页面Scale值发生改变时回调
         */
        @Override
        public void onScaleChanged(WebView view, float oldScale, float newScale) {
            super.onScaleChanged(view, oldScale, newScale);
        }

        /**
         * 是否在 WebView 内加载页面
         */
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return super.shouldOverrideUrlLoading(view, url);
        }

        /**
         * WebView 开始加载页面时回调,一次Frame加载对应一次回调
         */
        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
        }

        /**
         * WebView 完成加载页面时回调,一次Frame加载对应一次回调
         */
        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
        }

        /**
         * WebView 加载页面资源时会回调,每一个资源产生的一次网络加载,除非本地有当前 url 对应有缓存,否则就会加载。
         */
        @Override
        public void onLoadResource(WebView view, String url) {
            super.onLoadResource(view, url);
        }

        /**
         * WebView 可以拦截某一次的 request 来返回我们自己加载的数据,这个方法在后面缓存会有很大作用。
         */
        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
            return super.shouldInterceptRequest(view, request);
        }

        /**
         * WebView 访问 url 出错
         */
        @Override
        public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
            super.onReceivedError(view, request, error);
        }

        /**
         * WebView ssl 访问证书出错,handler.cancel()取消加载,handler.proceed()对然错误也继续加载
         */
        @Override
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
            super.onReceivedSslError(view, handler, error);
        }
    }

三、与JS交互

1、Android调用js方法:2种方式

js方法:

<script>
    function callJS(){
        alert("Android调用了JS的callJS方法");
    }
</script>

(1)第一种方法:loadUrl

wvWebview.loadUrl("javascript:callJS()");

(2)第二种方法:evaluateJavascript

wvWebview.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
                @Override
                public void onReceiveValue(String value) {
                    //此处为 js 返回的结果
                }
            });

因为该方法的执行不会使页面刷新,而第一种方法(loadUrl )的执行则会。Android 4.4 后才可使用

所以建议:两种方法混合使用,即Android 4.4以下使用方法1,Android 4.4以上方法2

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            wvWebview.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
                @Override
                public void onReceiveValue(String value) {
                    //此处为 js 返回的结果
                }
            });
        } else {//Android 4.4 前使用该方法
            wvWebview.loadUrl("javascript:callJS()");
        }

2、js调用Android方法:3种方式

(1)第一种方法:addJavascriptInterface

优点:使用简单

缺点:存在严重的漏洞问题

 

// js调用方式
<script>
    function callAndroid(){
        test.hello("js调用了android中的hello方法");
    }
</script>


//通过WebView设置Android类与JS代码的映射
wvWebview.addJavascriptInterface(new AndroidtoJs(), "test");
// 定义JS需要调用的方法
public class AndroidtoJs extends Object {
        // 被JS调用的方法必须加入@JavascriptInterface注解
        @JavascriptInterface
        public void hello(String msg) {
            System.out.println("JS调用了Android的hello方法");
        }
}

(2)第二种方法:通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url

 

//js调用
<script>
    function callAndroid(){
        document.location="js://webview?arg1=1&arg2=222";
    }
</script>


// andorid
// 在Android通过WebViewClient复写shouldOverrideUrlLoading ()
public class MyWebViewClient extends WebViewClient {
         /**
         * 是否在 WebView 内加载页面
         */
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            // 步骤2:根据协议的参数,判断是否是所需要的url
            // 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)
            //假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的)
            Uri uri = Uri.parse(url);
            // 如果url的协议 = 预先约定的 js 协议
            // 就解析往下解析参数
            if (uri.getScheme().equals("js")) {
                // 如果 authority  = 预先约定协议里的 webview,即代表都符合约定的协议
                // 所以拦截url,下面JS开始调用Android需要的方法
                if (uri.getAuthority().equals("webview")) {
                    // 步骤3:
                    // 执行JS所需要调用的逻辑
                    System.out.println("js调用了Android的方法");
                    // 可以在协议上带有参数并传递到Android上
                    HashMap<String, String> params = new HashMap<>();
                    Set<String> collection = uri.getQueryParameterNames();
                }
                return true;
            }
            return super.shouldOverrideUrlLoading(view, url);
        }
}

(3)第三种方法:通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息

 

//js调用
<script>
    function clickprompt(){
        var result = prompt("js://demo?arg1=111&arg2=222");
        alert("demo" + result);
    }
</script>

//android定义
public class MyWebChromeClient extends WebChromeClient {
    @Override
    public boolean onJsPrompt(WebView view, String url, String message, String             defaultValue, JsPromptResult result) {
            // 根据协议的参数,判断是否是所需要的url(原理同方式2)
            // 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)
            //假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的)
            Uri uri = Uri.parse(message);
            // 如果url的协议 = 预先约定的 js 协议
            // 就解析往下解析参数
            if (uri.getScheme().equals("js")) {
                // 如果 authority  = 预先约定协议里的 webview,即代表都符合约定的协议
                // 所以拦截url,下面JS开始调用Android需要的方法
                if (uri.getAuthority().equals("webview")) {
                    // 执行JS所需要调用的逻辑
                    System.out.println("js调用了Android的方法");
                    // 可以在协议上带有参数并传递到Android上
                    HashMap<String, String> params = new HashMap<>();
                    Set<String> collection = uri.getQueryParameterNames();

                    //参数result:代表消息框的返回值(输入值)
                    result.confirm("js调用了Android的方法成功啦");
                }
                return true;
            }
            return super.onJsPrompt(view, url, message, defaultValue, result);
        }
}

四、开源库

因为WebView 本身功能不是很完善而且用起来也是极其麻烦,所以这里推荐2个开源库:JsBridge 与AgentWeb 

JSBridge:https://www.jianshu.com/p/2ec3f06d6087

AgentWebhttps://www.jianshu.com/p/c80da1c41af7

WebView开源库地址:

https://github.com/lzyzsd/JsBridge 

https://github.com/Justson/AgentWeb 

 

注:参考文章

https://www.jianshu.com/p/3c94ae673e2a Webview 使用攻略

https://www.jianshu.com/p/fd61e8f4049e Webview 干货

https://www.jianshu.com/p/345f4d8a5cfa Android WebView与 JS 的交互方式

https://www.jianshu.com/p/3a345d27cd42 Android WebView 使用漏洞

https://www.jianshu.com/p/5e7075f4875f WebView 缓存机制

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值