WebView全面学习(二)-- Native与js双方通信
WebView全面学习(二)-- Native与js双方通信
Native与js通信的本质
Native与js通信的核心在于WebView.
两端的通信主要还是单向的。假如要完成js->Native->js那么就需要把这两种单向的通信结合起来使用。
两种通信的处理依旧是在Native端来完成
Native调用js的代码:(两种方式)
WebView.loadUrl()
优点:调用方式简单
缺点:获取返回值麻烦,效率低
WebView.evaluateJavascript()
优点:效率高
缺点:仅Android4.4以上版本适用
总结:目前市面上基本都会要求Android4.4以上包括许多大厂,所以最好采用WebView.evaluateJavascript方式进行Native->js调用,
如果考虑兼容性到4.4以下可以使用WebView.loadUrl方式
loadUrl方式实现
第一步:为了方便测试,这里把页面放在assets目录中,页面命名native2js.html,下面是页面的内容
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
// JS代码
<script>
// Android需要调用的方法
function callJs(){
alert("Android调用了JS的callJs方法");
}
</script>
</head>
</html>
第二部:loadUrl调用js的方法
public class MainActivity extends AppCompatActivity {
private WebView mWebview;
private WebSettings mWebSettings;
private Button btn;
private FrameLayout container;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
mWebview = new WebView(getApplicationContext());
//不要在xml中用标签!会引起内存泄漏
container = (FrameLayout) findViewById(R.id.webView1);
container.addView(mWebview);
btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mWebview.loadUrl("javascript:callJs()");
}
});
mWebSettings = mWebview.getSettings();
mWebSettings.setJavaScriptEnabled(true);
mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);
mWebview.loadUrl("file:///android_asset/native2js.html");
//设置不用系统浏览器打开,直接显示在当前Webview
mWebview.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
//设置WebChromeClient类
mWebview.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
//这里不采取弹窗,让用户从原生无感调用到js
Log.d("mainActivity", url + " msg " + message);
return true;
}
});
}
@Override
protected void onPause() {
mWebview.onPause();
super.onPause();
}
//销毁Webview
@Override
protected void onDestroy() {
if (mWebview != null) {
mWebview.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
mWebview.clearHistory();
((ViewGroup) mWebview.getParent()).removeView(mWebview);
mWebview.removeAllViews();
mWebview.destroy();
mWebview = null;
}
super.onDestroy();
}
}
特别注意:1.这种方式Native调用js方法一定要在 onPageFinished() 回调之后才能调用,否则不会调用到。
2.webView生成不要使用XML中标签的形式,并且在onDestory中进行上述销毁工作。否则会引起内存泄漏:
Activity ..... has leaked IntentReceiver org.chromium.content.browser.accessibility.LollipopBrowserAccessibilityManager$1@9a08817 that was originally registered here. Are you missing a call to unregisterReceiver()?
3.loadUrl这种方式来做native->js的调用会让页面每次都会刷新,如果页面加载较慢很容易引起屏幕闪一下或者白屏,效果较差
WebView.evaluateJavascript方式
// 只需将第一种的loadUrl()换成下面该方法即可 mWebView.evaluateJavascript("javascript:callJs()",
new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此处为 js 返回的结果
}});
总结:综上使用WebView.evaluateJavascript方式实现Native->js的调用最佳!考虑兼容性可以在4.4以下使用loadUrl方式!
final int version = Build.VERSION.SDK_INT;
if (version >= 19) {
mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此处为 js 返回的结果 }
});
} else {
mWebView.loadUrl("javascript:callJS()");
}
js调动Native的代码:(三种方式)
WebView.addJavascriptInterface()进行对象映射
优点:方式简单
缺点:Android4.2以下有漏洞
WebViewClient.shouldOverrideUrlLoading ()方法回调中拦截 url对url中采取和前端
商定协议,然后在收到带有协议的url时进行拦截,并调用原生的代码处理优点:可以和IOS统一调用方式,让前端调用方便,没有漏洞问题
缺点:需要提前和前端商定协议,从Native->js比较繁琐
WebChromeClient 在onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截js对话框alert()、confirm()、prompt() 中的消息
优点:可以和IOS统一调用方式,让前端调用方便,没有漏洞问题
缺点:需要提前和前端商定协议,用户感知较明显,会弹出对话框(当然可以在Native层选择不弹框,但这样会让代码复杂性上升)
总结: 目前市面上基本都会要求Android4.4以上包括许多大厂,所以第一种方式也可以选择。但是在4.2以下版本中会存在安全漏洞,不建议使用
目前主流采用的还是第二种和第三种方式,我们Hybrid方案考虑到Android和IOS两端一致性采用的是WebViewClient.shouldOverrideUrlLoading回调中拦截url的方式
WebView.addJavascriptInterface()方式
第一步:定义一个记录Native与js对象映射关系的类:Native2Js
public class Native2Js extends Object {
// 定义JS需要调用的方法
// 被JS调用的方法必须加入@JavascriptInterface注解
@JavascriptInterface
public void callNativeMethod1(String msg) {
System.out.println("JS调用了Native的callNativeMethod1方法");
}
}
第二步:js调用Native方法
//把js2native.html放到Android工程assets目录下,内容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
function callNative(){
// 由于对象映射,所以调用test对象等于调用Native映射的对象 jsCallNative.callNativeMethod1("js->native调用native的方法callNativeMethod1");
}
</script>
</head>
<body>
//点击按钮则调用native端方法
<button type="button" id="button1" onclick="callNative()"></button>
</body>
</html>
第三步:Native端准备接受js的调用
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = mWebView.getSettings();
// 允许Js交互
webSettings.setJavaScriptEnabled(true);
// 通过addJavascriptInterface()将Java对象映射到JS对象
//参数分别为native和js中的映射关系对象,通过这个方法的调用做到js和native方法互通
mWebView.addJavascriptInterface(new Native2Js(), "jsCallNative");
//AndroidtoJS类对象映射到js的test对象
// 加载JS代码
// 格式规定为:file:///android_asset/文件名.html
mWebView.loadUrl("file:///android_asset/js2native.html");
}
注意: 这中方式在4.2以下会有安全漏洞,所以不建议使用这种方式
WebViewClient.shouldOverrideUrlLoading()回调拦截
这种方式的主要流程如下:
1. WebViewClient 的回调方法shouldOverrideUrlLoading ()拦截 url
2. 检测url,看是否是和前端确定好的协议
3. 如果是就调用相应原生方法
第一步:js端准备
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>function callAndroid() {
/*约定的url协议为:company://hybrid?arg1=111&arg2=222*/
document.location = "company://hybrid?arg1=111&arg2=222";
}</script>
</head>
<body>
<button type="button" id="btn" onclick="callNative()">点击调用Native代码</button>
</body>
</html>
第二步:native端准备接收js调用
mWebview.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri = Uri.parse(url);
if (!uri.getScheme().equals("company")) {
return true;
}
switch (uri.getScheme()) {
case "company":
if (!uri.getAuthority().equals("hybrid")) {
super.shouldOverrideUrlLoading(view, url);
break;
}
//走到这里则认为如何我们的私有协议
Log.d("Hybrid","js调用了Native");
// 可以在协议上带有参数拿到
Set<String> params = uri.getQueryParameterNames();
//...后面可以针对相应的params做处理
break;
default:
view.loadUrl(url);
break;
}
return true;
}
});
注意: 这种方式的一个问题是js调用了native,但是没有返回到js.为了给js端回复,还需要结合native->js的调用
WebChromeClient中onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截js中对话框alert() confirm() prompt()的消息
注意:这种方式其实利用的是js调用alert confirm prompt方法时, WebChromeClient中相应的方法会有回调,其中alert没有返回值,confirm返回布尔值,true为确认,false为取消,prompt可以返回任何值, 所以一般都会利用prompt来做为通信手段
第一步:js端调用alert confirm prompt方法
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
function click(){
// 调用prompt()
var result=prompt("company://hybrid?arg1=111&arg2=222");
alert("demo " + result);
}
</script>
</head>
<body>
<button type="button" id="button1" onclick="click()">点击调用Android代码</button>
</body>
</html>
第二步:Native端准备
mWebView.setWebChromeClient(new WebChromeClient() {
// 参数message:promt()的内容(不是url),从js传来
// 参数result:输入框的返回值,回到js中去
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
Uri uri = Uri.parse(message);
if ( uri.getScheme().equals("conpany")) {
if (uri.getAuthority().equals("hybrid")) {
//拿到来自js的参数 Set<String> collection = uri.getQueryParameterNames();
//参数result:代表消息框的返回值(输入值),把结果ret返回给js端
String ret = "js调用了native,这是返回结果";
result.confirm(ret);
}
return true;
}
return super.onJsPrompt(view, url, message, defaultValue, result);
}});
总结
native->js
native->js最好的办法还是envaluateJavaScript的方式,如果要考虑4.2以下的兼容性,可以结合上loadUrl
js->native
多数情况下hybrid都是js->native的。因为js要用native的一些本地能力
针对目前的双端调用方式来看,js->native的调用采用协议的方式最合适,不论是shouldOverrideUrlLoading回调拦截(这种方式要结合native->js的方式返回给js端数据),还是onJsPrompt回调拦截都比较灵活
WebView全面学习(二)-- Native与js双方通信的更多相关文章
- H5+ 移动app学习之二 Native.js
Native.js技术,简称NJS,是一种将手机操作系统的原生对象转义,映射为JS对象,在JS里编写原生代码的技术.如果说Node.js把js扩展到服务器世界,那么Native.js则把js扩展到手机 ...
- Android中WebView与H5的交互,Native与JS方法互调
项目中经常用到WebView与H5的交互,一个是H5调本地方法,一个是本地调H5方法,在此记录一下. 首先,启用JS支持 //启用js支持 webSettings.setJavaScriptEnabl ...
- MongoDB Native Node.js Driver
写在前面 最近读<node.js学习指南>,对于mongodb没有介绍太多的工作原理,但是对于一个前端开发者,即使你还没有用过这种数据库也可以让你很好的理解和使用 一本非常好的 ...
- WebView全面学习(一)--常用类和方法
WebView全面学习(一)--常用类和方法 WebView本质上是一个View,他基于webkit引擎来展示web页面 在Android不同的版本webkit内核有所区别,从Android版本上看, ...
- emberjs学习二(ember-data和localstorage_adapter)
emberjs学习二(ember-data和localstorage_adapter) 准备工作 首先我们加入ember-data和ember-localstorage-adapter两个依赖项,使用 ...
- ReactJS入门学习二
ReactJS入门学习二 阅读目录 React的背景和基本原理 理解React.render() 什么是JSX? 为什么要使用JSX? JSX的语法 如何在JSX中如何使用事件 如何在JSX中如何使用 ...
- TweenMax动画库学习(二)
目录 TweenMax动画库学习(一) TweenMax动画库学习(二) TweenMax动画库学习(三) Tw ...
- Hbase深入学习(二) 安装hbase
Hbase深入学习(二) 安装hbase This guidedescribes setup of a standalone hbase instance that uses the local fi ...
- SpringMVC入门学习(二)
SpringMVC入门学习(二) ssm框架 springMVC 在上一篇博客中,我简单介绍了一下SpringMVC的环境配置,和简单的使用,今天我们将进一步的学习下Springmvc的操作. mo ...
随机推荐
- c# winform DataGridView 单元格的屏幕位置
首先取得DataGridView的坐标位置:int dgvX = dataGridView1.Location.X;int dgvY = dataGridView1.Location.Y;然后取得选中 ...
- DAL 层引用 System.Net.Http ,引发的一阵心慌
快下班的时候 代码data 数据层编译失败,引起整个解决方案全部失败:其他同事虽然vs 版本不同,但是都能编译通过:考虑到今天更改过vs 的设置,把今天更改的设置全部都恢复,结果还是不行.最后直接恢复 ...
- [hiho1584]Bounce
题意:找出图中经过一次的格子个数. 解题关键: 组合数学的思想:先找出总的经过格子的次数,然后减去2倍的经过2次的格子个数. 1.总的求法:将长延展,当延展到n倍时,能够恰好到达右边的两个端点,则总格 ...
- [hdu3586]Information Disturbing树形dp+二分
题意:给出一棵带权无向树,以及给定节点1,总约束为$m$,找出切断与所有叶子节点联系每条边所需要的最小价值约束. 解题关键:二分答案,转化为判定性问题,然后用树形dp验证答案即可. dp数组需要开到l ...
- 报错:在做往下拉选里面拼接数据的时候 3个下拉选显示一个值 原因 @scope(单例)或者没配默认单例
解决 @scope(多例) 这是因为造成线程并发访问不安全
- skb_store_bits() 和 skb_copy_bits()
int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len); int skb_store_bits(c ...
- 【机器学习】k近邻算法(kNN)
一.写在前面 本系列是对之前机器学习笔记的一个总结,这里只针对最基础的经典机器学习算法,对其本身的要点进行笔记总结,具体到算法的详细过程可以参见其他参考资料和书籍,这里顺便推荐一下Machine Le ...
- 第3章 编写ROS程序-2
1.发布者程序 在本节中,我们将看到如何发送随机生成的速度指令到一个turtlesim海龟,使它漫无目的地巡游.这个程序的源文件称为pubvel,这个程序展示了从代码中发布消息涉及的所有要素. 其代码 ...
- 扩展thinkphp5的redis类方法
笔者在开发时发现,thinkphp5的自带redis类方法,只有简单的读取缓存.写入缓存的基本方法,远不能满足我们业务的需求.redis本身支持五种数据类型,string(字符串).hash(哈希). ...
- htons和htonl函数具体应用
htons和htonl函数具体应用 htons和htonl函数,是用来将主机字节顺序转换为网络字节顺序在进行网络抓包时,抓到的包的数据是网络字节顺序,在进行编程时,要进行主机字节顺序和网络字节顺序间的 ...