老早之前就想总结下Webview相关的知识点了,因为互联网大潮中,很多APP都会使用到Webview,像那些不计其数的电商APP,无一例外的使用Webview;或者一些非电商APP中的像广告页面,注册协议页面都会用到;最后因为一些事情拖到现在才做,感觉事情真不能拖,越往后推越做不了,罪过罪过。

怎么总结Webview呢

1.简单介绍

2.WebView/WebViewClient/WebChromeClient api介绍

3.简单使用

4.JS调用Android本地

5.Android调用JS方法

6.缓存处理及性能优化

7.webview使用注意点

webview系列文章

Android之WebView/WebViewClient/WebChromeClient简介 API详述 【一】

Android之WebView/WebViewClient/WebChromeClient 使用样例 【二】

Android之WebView Android调用JS方法 JS调用Android方法 【三】

Android之WebView 缓存处理 性能优化【四】

Android之WebView 使用注意点 JS注入漏洞问题 内存优化【五】

4.JS调用Android

方法主要有三种

1.通过Webview的addJavaScriptInterface方法注入java对象

2.通过WebViewClient的shouldOverrideUrlLoading方法拦截url,这个是用的最普遍的

3.通过WebChromeClient 的onJsAlert、onJsConfirm、onJsPrompt 提示接口进行相关操作

我们先看第一种 addJavaScriptInterface 方法

* 先编写Android端

1.布局编写

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/white"><WebViewandroid:id="@+id/webview"android:layout_width="match_parent"android:layout_height="280dp"/><TextViewandroid:id="@+id/result"android:layout_width="match_parent"android:layout_height="40dp"android:layout_marginTop="20dp"android:textSize="18sp"android:textColor="@color/colorAccent"/></LinearLayout>

布局很简单,就是一个webview和一个接受js返回值的textview

2.既然是注入java对象,那就编写这个对象

public class JSInterface {private String TAG = "JSInterface";// 定义JS需要调用的方法// 被JS调用的方法必须加入@JavascriptInterface注解@JavascriptInterfacepublic void setValue(String value) {Log.e("JSInterface","setValue value="+value);}
}

3.注入java对象,也就是添加js接口

private void initWebview() {WebSettings webSettings = mWebView.getSettings();// 设置与Js交互的权限webSettings.setJavaScriptEnabled(true);// 通过addJavascriptInterface()将Java对象映射到JS对象//参数1:java对象//参数2:Java对象在js里的对象名,也就是这个对象在js里叫啥mWebView.addJavascriptInterface(new JSInterface(), "mango");mWebView.loadUrl("file:///android_asset/javascript_android_2.html");}

到这里客户端就写完了。

* 接下来我们编写HTML部分

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebView</title><style type="text/css">body{background-color: rgb(210, 154, 28)}.btn{line-height: 40px;width: 80px;margin: 10px;background: rgb(238, 244, 233)}
</style>
</head><body><h1 >JS调用Android</h1>
<div >
<span >请输入要传递给Android的值</span>
<input type="text" id="input"/>
</div><div id="btn" class="btn" >
<span >click</span>
</div><!-- javascript逻辑就是点击button将输入框的值返回给android客户端 -->
<script type="text/javascript">var btnEle = document.getElementById('btn');var inputEle = document.getElementById('input');// 给按钮添加一个click事件监听btnEle.addEventListener("click",function () {var value = inputEle.value;/ / 在页面show一下输入的值// alert(value);// 先判断android客户端传入的java对象存不存在if(window.mango){mango.setValue(value);}else{alert("mango not found");}});</script></body>

具体逻辑都注释了,我们先把这个html在浏览器运行看看,完全符合最后那个判断。

* 然后我们运行客户端

我这用的是模拟器,在输入框输入值后,看看在JsInterface类的setValue方法打印的log

06-11 21:58:53.863 3770-3878/? D/JSInterface: setValue value=2552

这里可以得出:

1.android中原生方法成功被JS调用了

2.这个被调用的方法所在线程并不是在主线程,看上面这个日志 3770-3878 ,3770表示主线程id,3878表示子线程id;这就表示它是一个非UI线程,不能在这里更新UI.要更新UI需要通过接口或者Handler,如下

@JavascriptInterface
public void setValue(String value) {Log.d("JSInterface","setValue value="+value);Message msg = mHandler.obtainMessage();msg.what = UPDATE_TEXT;msg.obj = value;mHandler.sendMessage(msg);
}
Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);int tag = msg.what ;if (UPDATE_TEXT == tag) {String result = (String) msg.obj;mResult.setText(result);}}
};

这样就实现了android 和 JS 交互,但是实现功能的同时也带了安全问题,通过注入的 Java 类作为桥梁,JS 就可以利用这个漏洞,具体什么我们放在最后总结。

看第二种方法 shouldOverrideUrlLoading

工作逻辑是

1.通过shouldOverrideUrlLoading方法拦截URL,

2.解析url

3.根据约定好的规则去匹配解析后的url,如果规则相同,就进行操作

先写好html文件

<!DOCTYPE html>
<html lang="en" dir="ltr"><head><meta charset="utf-8"><title>webview</title><style media="screen">body{background-color: rgb(224, 112, 57);}</style>
</head><body><h1>Js调用Android二</h1><button type="button" id="btn" onclick="callAndroid()">点击调用Android方法</button><script type="text/javascript">function callAndroid(){document.location = "js://webview?id=1&value=2";}</script></body></html>

再写客户端

WebSettings webSettings = mWebView.getSettings();// 设置与Js交互的权限webSettings.setJavaScriptEnabled(true);mWebView.loadUrl("file:///android_asset/jscallandroid.html");// 复写WebViewClient类的shouldOverrideUrlLoading方法// 设置允许JS弹窗webSettings.setJavaScriptCanOpenWindowsAutomatically(true);mWebView.setWebViewClient(new WebViewClient() {@Overridepublic boolean shouldOverrideUrlLoading(WebView view, String url) {Log.e(TAG,"shouldOverrideUrlLoading url="+url);// 根据协议的参数,判断是否是所需要的url// 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)//假定传入进来的 url = "js://webview?id=1&value=2"(同时也是约定好的需要拦截的)Uri uri = Uri.parse(url);// 如果url的协议 = 预先约定的 js 协议// 就解析往下解析参数if ( uri.getScheme().equals("js")) {// 如果 authority  = 预先约定协议里的 webview,即代表都符合约定的协议// 所以拦截url,下面JS开始调用Android需要的方法if (uri.getAuthority().equals("webview")) {// 执行JS所需要调用的逻辑Toast.makeText(mContext,"js调用了Android的方法",Toast.LENGTH_LONG).show();// 可以在协议上带有参数并传递到Android上HashMap<String, String> params = new HashMap<>();Set<String> collection = uri.getQueryParameterNames();//如果js需要返回值,就从这里再次执行js方法把结果返回回去
//                            mWebView.loadUrl("javascript:returnResult(" + "result" + ")");}return true;}return super.shouldOverrideUrlLoading(view, url);}});

看看打印的日志

06-11 22:57:29.033 6256-6256/? E/AndroidCallJSFrag: shouldOverrideUrlLoading url=js://webview?id=1&value=2

这个url与js里的一样,这就是双方约定好,然后一个一个解析url结构,然后就可以进行相关操作;并且看到6256-6256,说明这是在UI线程回调。

这种方法不会出现第一种方法出现的漏洞。

再看第三种方法  通过WebChromeClient 的onJsAlert、onJsConfirm、onJsPrompt 

当js里调用alert(),confirm(),prompt()方法的时候,我们可以通过WebChromClient类的onJsAlert、onJsConfirm、onJsPrompt三个方法去拦截消息,然后解析。

这三个js方法有点不同,

alert():弹出一个警告框,没有返回值

confirm():弹出确认框,有两个状态返回值(ok/no)

prompt():弹出输入框,可设置任意返回值,这就是最灵活的,也是我们最常用的

我们先写html

<!DOCTYPE html>
<html lang="en" dir="ltr"><head><meta charset="utf-8"><title>WebView</title><style media="screen">body{background-color: rgb(224, 112, 57);}</style></head><body><h1>Js调用Android三</h1><button type="button" id="btn" onclick="clickPrompt()">clickPrompt调用android</button><script type="text/javascript">function clickPrompt(){// 通过prompt调用android,获取返回值resultvar result = prompt("js://webview?id=1&value=2");// 将获取的android返回值通过alert弹出框显示出来alert(result);}</script></body></html>

再写android客户端

WebSettings webSettings = mWebView.getSettings();
// 设置与Js交互的权限
webSettings.setJavaScriptEnabled(true);
mWebView.loadUrl("file:///android_asset/jscallandroid-2.html");
// 复写WebViewClient类的shouldOverrideUrlLoading方法
// 设置允许JS弹窗
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
mWebView.setWebChromeClient(new WebChromeClient(){@Overridepublic boolean onJsAlert(WebView view, String url, String message, JsResult result) {Log.d(TAG,"onJsAlert");return super.onJsAlert(view, url, message, result);}@Overridepublic boolean onJsConfirm(WebView view, String url, String message, JsResult result) {return super.onJsConfirm(view, url, message, result);}/*** 拦截js的prompt弹出框* @param view* @param url* @param message*          js里promt()方法的参数内容,不是url* @param defaultValue* @param result*          代表输入框的值* @return*/@Overridepublic boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {Log.d(TAG,"onJsPrompt message="+message);return super.onJsPrompt(view, url, message, defaultValue, result);}});

我们如果在onJsPrompt不做处理,让webview自己处理看看什么情况

跟js代码里的逻辑是一样的,先通过prompt弹出个输入框,而且onJsPrompt方法里的日志打印了出来

06-12 22:46:51.855 5010-5010/? D/JsCallAndroid: onJsPrompt message=js://webview?id=1&value=2

看到线程id说明这个回调是在UI线程

当输入之后点击确定

也是跟js里逻辑一样,把输入内容弹出框显示

然后我们在onJsPrompt方法里对Message内容进行解析过滤,如果是我们自己想要的,进行相应处理

/*** 拦截js的prompt弹出框* @param view* @param url* @param message*          js里promt()方法的参数内容,不是url* @param defaultValue* @param result*          代表输入框的返回值* @return*/
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {Log.d(TAG,"onJsPrompt url = " + url +",message="+message );// 根据协议的参数,判断是否是所需要的url// 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)//假定传入进来的 url = "js://webview?id=1&value=2"(同时也是约定好的需要拦截的)Uri uri = Uri.parse(message);// 如果url的协议 = 预先约定的 js 协议// 就解析往下解析参数if ( uri.getScheme().equals("js")) {// 如果 authority  = 预先约定协议里的 webview,即代表都符合约定的协议// 所以拦截url,下面JS开始调用Android需要的方法if (uri.getAuthority().equals("webview")) {//// 执行JS所需要调用的逻辑Log.d(TAG,"js调用了Android的方法");// 可以在协议上带有参数并传递到Android上HashMap<String, String> params = new HashMap<>();Set<String> collection = uri.getQueryParameterNames();Iterator<String>  iterator = collection.iterator();while (iterator.hasNext()) {String key = iterator.next();String value = uri.getQueryParameter(key);Log.d(TAG,"key="+key+",value="+value);params.put(key,value);}//参数result:代表消息框的返回值(输入值)result.confirm("js调用了Android的方法成功啦");}return true;}return super.onJsPrompt(view, url, message, defaultValue, result);
}

我们点击js里button后回回调android里的onJsPrompt方法,这里我们可以获取js传给我们的数据

看日志

06-12 22:56:15.525 5379-5379/? D/AndroidCallJSFrag: key=id,value=1

06-12 22:56:15.525 5379-5379/? D/AndroidCallJSFrag: key=value,value=2

再与js代码中的prompt方法比较

var result = prompt("js://webview?id=1&value=2");

可以看到获取的值是一样的。

至此JS调用Android的三种方法总结完毕。

接下来讲述第五步

5.Android调用JS

android调用js主要有两种方法

1.使用webview的loadurl

2.使用webview的evaluateJavascript

我们先看第一种方法

先把html代码编写完成

<!DOCTYPE html>
<html lang="en" dir="ltr"><head><meta charset="utf-8"><title>WebView</title><style >body{background-color: rgb(230, 105, 34);}</style></head><body><h1 > android调用js</h1><script type="text/javascript">//提供一个方法供android客户端调用function callJS(){//如果android调用js成功就用弹出框提示alert("android成功调用了js");//如果android调用js成功就返回值return "success";}</script></body>
</html>

然后在客户端编写

private void initWebview() {WebSettings set = mWebView.getSettings();// 设置与Js交互的权限set.setJavaScriptEnabled(true);// 设置允许JS弹窗set.setJavaScriptCanOpenWindowsAutomatically(true);// 由于设置了弹窗检验调用结果,所以需要支持js对话框// 通过设置WebChromeClient对象处理JavaScript的对话框//设置响应js 的Alert()函数mWebView.setWebChromeClient(new WebChromeClient());mWebView.setWebViewClient(new WebViewClient(){@Overridepublic void onPageFinished(WebView view, String url) {super.onPageFinished(view, url);Log.e(TAG,"onPageFinished url="+url);//JS代码调用一定要在 onPageFinished() 回调之后才能调用,否则不会调用。mWebView.loadUrl("javascript:callJs()");}});mWebView.loadUrl("file:///android_asset/androidcalljs.html");
}

我们看第二种方法:

这个方法比第一种方法使用更高效,可以获取js返回值,要4.4以后才可以用

html不用变,只需在调用的客户端修改下

private void initWebview() {WebSettings set = mWebView.getSettings();// 设置与Js交互的权限set.setJavaScriptEnabled(true);mWebView.loadUrl("file:///android_asset/androidcalljs.html");// 设置允许JS弹窗set.setJavaScriptCanOpenWindowsAutomatically(true);// 由于设置了弹窗检验调用结果,所以需要支持js对话框// 通过设置WebChromeClient对象处理JavaScript的对话框//设置响应js 的Alert()函数mWebView.setWebChromeClient(new WebChromeClient());}@OnClick(R.id.tv_callJs)
public void callJS(){final int version = Build.VERSION.SDK_INT;if (version < 18) {mWebView.loadUrl("javascript:callJS()");//低版本使用这个方法} else {mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {@Overridepublic void onReceiveValue(String value) {//此处为 js 返回的结果Log.e(TAG,"value="+value);}});}
}

看看打印的日志

06-13 20:56:34.478 9464-9464/? E/AndroidCallJSFrag: value="success"

获取js的返回值成功,并且这个回调也是在UI线程

到这里本节就讲完了

相关代码已托管到GitHub

获取去这里下载我的下载

Android开发-WebView中实现Android调用JS JS调用Android 【三】相关推荐

  1. android 连续调用js方法,Android的WebView中的JavascriptInterface:对JS的多次调用会导致死锁...

    这是我用过的整个Java代码.我将在下面更详细地解释... public class Test7 extends Activity { //debug private final static Str ...

  2. android工程师饱和,Android开发是否饱和了?你所不了解的Android开发

    原标题:Android开发是否饱和了?你所不了解的Android开发 Android开发是否饱和了?是很多人关心的问题,对于想要从事Android开发的人更是头等大事.如今,就业难成横亘在求职者面前的 ...

  3. Android开发-WebView的缓存处理和性能优化 实现H5页面秒开【四】

    前言 老早之前就想总结下Webview相关的知识点了,因为互联网大潮中,很多APP都会使用到Webview,像那些不计其数的电商APP,无一例外的使用Webview:或者一些非电商APP中的像广告页面 ...

  4. Android开发-WebView/WebViewClient/WebChromeClient简介 API详述 【一】

    老早之前就想总结下Webview相关的知识点了,因为互联网大潮中,很多APP都会使用到Webview,像那些不计其数的电商APP,无一例外的使用Webview:或者一些非电商APP中的像广告页面,注册 ...

  5. Android开发经验谈-很少有人会告诉你的Android开发基本常识,经验谈android

    转载:http://www.android100.org/html/201507/15/165084.html Android开发经验谈-很少有人会告诉你的Android开发基本常识,经验谈andro ...

  6. android开发学习之路——连连看之加载图片(三)

    正如前面AbstractBoard类的代码中看到的,当程序需要创建N个Piece对象时,程序会直接调用ImageUtil的getPlayImages()方法去获取图片,该方法将会随机从res\ dra ...

  7. 001 初学android开发,从搭建环境开始(jdk+eclipse+android sdk+windows7)

    001 初学android开发,从搭建环境开始(jdk+eclipse+android sdk+windows7) 毕业就一直从事.Net开发,之后做管理,做产品设计,疏于编码. 最近打算买个小米手机 ...

  8. js android打电话,Android开发webview与js的交互总结【安卓巴士博文大赛】

    一些应用为了节省开发时间,会开用Android.iOS内嵌HTML方式进行开发,在涉及到打电话.发短信这些Android原生功能时,需要涉及到webView中js与ANdroid的交互.这里结合我做过 ...

  9. Android—在WebView中下载Blob协议文件

    之前有个需求是要下载Blob协议的gif,让我苦恼了好久.平时下载http协议的文件时直接获取输入流即可,但是Java无法获得Blob协议的文件流,无法直接处理.不过JavaScript处理Blob协 ...

最新文章

  1. 『TensorFlow』数据读取类_data.Dataset
  2. 在CentOS 6.3中安装与配置JDK-7
  3. C++ 11 深度学习(三)范围for、new内存动态分配、nullptr
  4. flutter的按钮如何变为不可选中_Flutter 61: 图解基本 Button 按钮小结 (一)
  5. 我是一个*** (十三)
  6. office2010过期解决办法
  7. 如何开发一个完整的JavaScript组件
  8. AD09画pcb板时遇到的问题
  9. 微信小程序笔记——滚动计数器
  10. 高考数学圆锥曲线总结贴+杂题巧解
  11. Android性能优化-Apk瘦身(1)
  12. Kimball维度建模
  13. 文件上传漏洞攻击与防御
  14. OpenGL(十二)——Qt OpenGL绕着坐标轴旋转多边形
  15. Allegro Design Entry CIS 和 Orcad Capture CIS 区别
  16. Springboot 整合dubbo、zookeeper
  17. [Python人工智能] 十七.Keras搭建分类神经网络及MNIST数字图像案例分析
  18. 服务器存储系统交付清单,附件三 软硬件交付清单(1).docx
  19. 百度地图根据经纬度绘制轨迹
  20. 常见的进制和进位规则

热门文章

  1. html鼠标移动自动展开,JS实现鼠标滑过折叠与展开菜单效果代码
  2. 树莓派GPIO 基础(一)
  3. 做iOS自动化测试必须知道的一些知识
  4. RabbitMQ第二话 -- Springboot基于四种Exchange(Direct、Fanout、Topic、Heders、延时队列)的实现和多虚拟主机下的生产消费者实现
  5. 获取android模拟器的IP地址
  6. 上海证券交易所-债券品种介绍
  7. JavaScript 动态表格操作
  8. 【imessage群发苹果推信】[iMessageSuspic](Apple推送服务)使用信息推送服务
  9. 国家级发明专利专利 - 崔博文
  10. 流程设计建模方法:流程的需求梳理之流程级别梳理