Android WebView

        Android WebView

        一、简介

        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.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 additionalHttpHeaders);// 加载制定url并携带http header数据。。

wvWebview.stopLoading();// 停止 WebView 当前加载。

wvWebview.freeMemory();// 释放内存,不过貌似不好用。

wvWebview.clearFormData();// 清除自动完成填充的表单数据。需要注意的是,该方法仅仅清除当前表单域自动完成填充的表单数据,并不会清除WebView存储到本地的数据。

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

finalString filesDir = getContext().getFilesDir().getPath();

finalString 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(newMyWebChromeClient());

publicclassMyWebChromeClientextendsWebChromeClient{

/**输出 Web 端日志 */

@Override

publicbooleanonConsoleMessage(ConsoleMessage consoleMessage){

returnsuper.onConsoleMessage(consoleMessage);

        }

/** * 当前 WebView 加载网页进度 */

@Override

publicvoidonProgressChanged(WebView view,intnewProgress){

super.onProgressChanged(view, newProgress);

        }

/*** Js 中调用 alert() 函数,产生的对话框*/

@Override

publicbooleanonJsAlert(WebView view, String url, String message, JsResult result){

returnsuper.onJsAlert(view, url, message, result);

        }

/** * 处理 Js 中的 Confirm 对话框*/

@Override

publicbooleanonJsConfirm(WebView view, String url, String message, JsResult result){

returnsuper.onJsConfirm(view, url, message, result);

        }

/** * 处理 JS 中的 Prompt对话框 */

@Override

publicbooleanonJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result){

returnsuper.onJsPrompt(view, url, message, defaultValue, result);

        }

/*** 接收web页面的icon*/

@Override

publicvoidonReceivedIcon(WebView view, Bitmap icon){

super.onReceivedIcon(view, icon);

        }

/** * 接收web页面的 Title */

@Override

publicvoidonReceivedTitle(WebView view, String title){

super.onReceivedTitle(view, title);

        }

    }

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

wvWebview.setWebViewClient(newMyWebViewClient());

publicclassMyWebViewClientextendsWebViewClient{

/*** 当WebView得页面Scale值发生改变时回调 */

@Override

publicvoidonScaleChanged(WebView view,floatoldScale,floatnewScale){

super.onScaleChanged(view, oldScale, newScale);

        }

/** * 是否在 WebView 内加载页面 */

@Override

publicbooleanshouldOverrideUrlLoading(WebView view, String url){

            view.loadUrl(url);

returnsuper.shouldOverrideUrlLoading(view, url);

        }

/** * WebView 开始加载页面时回调,一次Frame加载对应一次回调 */

@Override

publicvoidonPageStarted(WebView view, String url, Bitmap favicon){

super.onPageStarted(view, url, favicon);

        }

/** * WebView 完成加载页面时回调,一次Frame加载对应一次回调 */

@Override

publicvoidonPageFinished(WebView view, String url){

super.onPageFinished(view, url);

        }

/*** WebView 加载页面资源时会回调,每一个资源产生的一次网络加载,除非本地有当前 url 对应有缓存,否则就会加载。 */

@Override

publicvoidonLoadResource(WebView view, String url){

super.onLoadResource(view, url);

        }

/** * WebView 可以拦截某一次的 request 来返回我们自己加载的数据,这个方法在后面缓存会有很大作用。*/

@Override

publicWebResourceResponseshouldInterceptRequest(WebView view, WebResourceRequest request){

returnsuper.shouldInterceptRequest(view, request);

        }

/** * WebView 访问 url 出错 */

@Override

publicvoidonReceivedError(WebView view, WebResourceRequest request, WebResourceError error){

super.onReceivedError(view, request, error);

        }

/*** WebView ssl 访问证书出错,handler.cancel()取消加载,handler.proceed()对然错误也继续加载*/

@Override

publicvoidonReceivedSslError(WebView view, SslErrorHandler handler, SslError error){

super.onReceivedSslError(view, handler, error);

        }

    }

        三、与JS交互

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

    js方法:

functioncallJS(){ 

       alert("Android调用了JS的callJS方法");

 }

        (1)第一种方法:loadUrl

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

        (2)第二种方法:evaluateJavascript

wvWebview.evaluateJavascript("javascript:callJS()",newValueCallback() {

@Override

publicvoidonReceiveValue(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()",newValueCallback() {

@Override

publicvoidonReceiveValue(String value){

//此处为 js 返回的结果

                }

            });

}else{//Android 4.4 前使用该方法

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

        }

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

        (1)第一种方法:addJavascriptInterface

        优点:使用简单

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

// js调用方式

    function callAndroid(){

        test.hello("js调用了android中的hello方法");

    }

//通过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调用

    function callAndroid(){

        document.location="js://webview?arg1=1&arg2=222";

    }

// 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 params = new HashMap<>();

                    Set collection = uri.getQueryParameterNames();

                }

                return true;

            }

            return super.shouldOverrideUrlLoading(view, url);

        }

}

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

//js调用

    function clickprompt(){

        var result = prompt("js://demo?arg1=111&arg2=222");

        alert("demo" + result);

    }

//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 params = new HashMap<>();

                    Set collection = uri.getQueryParameterNames();

                    //参数result:代表消息框的返回值(输入值)

                    result.confirm("js调用了Android的方法成功啦");

                }

                return true;

            }

            return super.onJsPrompt(view, url, message, defaultValue, result);

        }

}

    四、开源库

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

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/3c94ae673e2aWebview 使用攻略

https://www.jianshu.com/p/fd61e8f4049eWebview 干货

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

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

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

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

推荐阅读更多精彩内容