android webview js交互,Android与JS交互_Ferrybunny的博客-程序员宅基地

技术标签: android webview js交互  

现在越来越多的App是混合开发,很需要原生与网页交互,下面介绍如何交互以及有可能出现的坑。

Java调用JS方法

对于Android调用JS方法的方式有2种:

通过WebView的loadUrl()

通过WebView的evaluateJavascript()

方式一:loadUrl()

第一步:设置与Js交互的权限

WebSettings webSettings = mWebView.getSettings();

// 设置与Js交互的权限

webSettings.setJavaScriptEnabled(true);

第二步:设置加载网页

mWebview.loadUrl("加载的网页");

第三步:调用JS方法

通过使用mWebview.loadUrl 调用“javascript:" + 方法

如果JS这样定义的

function callJS(){

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

}

function callJSParameter(message){

alert("Android调用JS的有参callJS方法,参数为"+message);

}

在Java代码中

//调用无参

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

//调用有参

mWebView.loadUrl("javascript:callJSParameter('测试')");

方式一注意事项(也就是坑)

调用JS方法时一定要在onPageFinished()回调之后才能调用,否则不会调用。

onPageFinished()属于WebViewClient类的方法,主要在页面加载结束时调用

调用JS方法时一定要在主线程,否则会崩溃异常。

java.lang.RuntimeException: java.lang.Throwable: A WebView method was called on thread 'Thread-18022'. All WebView methods must be called on the same thread.

如果非要在子线程调用,也要将其转换到主线程中去:

mWebView.post(new Runnable() {

@Override

public void run() {

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

}

});

调用的带参数的JS方法时,参数类型如果为String ,切记使用单引号( ') 包裹;如果为数组类型则不用,如:javascript:callJs([01, 02, 03]);如果为其他复杂类型则可以转换为 Json 字符串的形式传递。

方式二:evaluateJavascript()

*执行该方法不会使页面刷新,而第一种方法(loadUrl )的执行则会,但是Android 4.4 后才可使用,兼容性要求偏高。

JS代码

function callJSReturn(message){

return message;

}

Java代码

mWebView.evaluateJavascript("javascript:callJSReturn('方式二调用JS')", new ValueCallback() {

@Override

public void onReceiveValue(String value) {

//此处为 js 返回的结果

Toast.makeText(MainActivity.this, value, Toast.LENGTH_SHORT).show();

}

});

两种方式对比

调用方式

优点

缺点

使用场景

loadUrl()

兼容性好

获取返回值麻烦

不需要获取返回值

evaluateJavascript()

只能从4.4(18)以上使用

获取返回值简单

4.4(18)以上

JS调用Java

JS调用Java的方式有三种

通过WebView的addJavascriptInterface()

通过WebViewClient 的shouldOverrideUrlLoading()方法回调拦截 url

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

方式一:addJavascriptInterface()

JS代码

LYJ

function testCallAndroid(){

test.callAndroid("JS调用了Android方法");

}

点击按钮通过方式一调用Android方法

第一步:定义一个JS调用的方法

@JavascriptInterface

public void callAndroid(String message){

Toast.makeText(CallAndroidActivity.this,message,Toast.LENGTH_SHORT).show();

}

1.必须要有@JavascriptInterface,在 Android4.2(17) 及更高版本的系统中,任何暴露给 Js 访问的 Java 接口都需要添加这个注解,否则会报异常:Uncaught TypeError: Object [object Object] has no method ‘XXX’。系统在之前的版本中有漏洞,Js 可以通过反射的方式访问注入 WebView 中的 Java 对象的 public 类型 field 和 method,从而随意修改宿主程序,所以为了安全增加了这个注解。想更深入的了解参考这篇文章你不知道的 Android WebView 使用漏洞

2.方法名切记要和JS调用的方法名一样

第二步将Java对象映射到JS对象

mWebView.addJavascriptInterface(this, "test");

第一个参数是写有JS调用方法的Android对象,第二个参数是JS调用的对象名,切记跟网页上的一致。

方式一注意事项

Js 调用 Java 方法时,不是在主线程 (Thread Name:main) 中运行的,而是在一个名为 JavaBridge 的线程中执行的,如果需要 Java 继续回调 Js,千万别在 JavascriptInterface 方法体中直接执行 loadUrl() 方法,而是像前面一样进行线程切换操作。

方式二:WebViewClient 的shouldOverrideUrlLoading()方法

JS代码

function testTwoCallAndroid(){

document.location = "js://webview?key1=value1&key2=value2";

}

点击按钮通过方式二调用Android方法

Java代码

// 复写WebViewClient类的shouldOverrideUrlLoading方法

mWebView.setWebViewClient(

new WebViewClient() {

@Override

public boolean shouldOverrideUrlLoading(WebView view, String url) {

// 第一步:根据协议的参数,判断是否是所需要的url

// 一般根据scheme(协议格式) & authority(协议名)判断

//假定传入进来的 url = "js://webview?key1=value1&key2=value2"(同时也是约定好的需要拦截的)

Uri uri = Uri.parse(url);

// 第二步:如果url的协议 = 预先约定的 js 协议,就解析往下解析参数

if (uri.getScheme().equals("js")) {

//第三步:再判断,如果 authority = 预先约定协议里的 webview,即代表都符合约定的协议

// 所以拦截url,下面JS开始调用Android需要的方法

if (uri.getAuthority().equals("webview")) {

// 第四步:

// 执行JS所需要调用的逻辑

// 获取协议执行的参数

HashMap params = new HashMap<>();

Set collection = uri.getQueryParameterNames();

for (String key : collection){

String value = uri.getQueryParameter(key);

Log.d("==","key = " + key + ",value = " + value);

params.put(key,value);

}

}

return true;

}

return super.shouldOverrideUrlLoading(view, url);

}

}

);

}

注释比较清楚,就按注释的步骤走。

shouldOverrideUrlLoading(WebView view, String url)在Android 7.0(24)过时,官方改为shouldOverrideUrlLoading (WebView view,

WebResourceRequest request)。然后获取Uri改为Uri uri = request.getUrl();。

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

方法

作用

返回值

备注

alert()

弹出警告框

没有

在文本加入/n可换行

confirm()

弹出确认框

两个

返回boolean值,true代表确认,false代表取消

prompt()

弹出输入框

任意

点击确认返回输入框的值,点击取消返回null

下面的例子将用拦截 JS的输入框(即prompt()方法)说明,其他两种与其类似 :

JS代码

function testPromptCallAndroid(){

var result=prompt("js://prompt?key1=value1&key2=value2");

alert("方式三 " + result);

}

点击按钮通过方式三调用Android方法

Java代码

mWebView.setWebChromeClient(

new WebChromeClient() {

// 拦截输入框(原理同方式2)

// 参数message:代表promt()的内容(不是url)

// 参数result:代表输入框的返回值

@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("prompt")) {

// 执行JS所需要调用的逻辑

// 可以在协议上带有参数并传递到Android上

HashMap params = new HashMap<>();

Set collection = uri.getQueryParameterNames();

for (String key : collection) {

String value = uri.getQueryParameter(key);

Log.d("==", "key = " + key + ",value = " + value);

params.put(key, value);

}

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

result.confirm("js通过方式三调用了Android的方法成功");

}

return true;

}

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

}

}

);

整体上跟上一个方式相差不多。

其他

如果需要回调这种需求,你只要android的异步回调中,使用loadUrl调用js的相关方法。

如果需要给JS返回值,除了方式三以外,还可以这么做

JS代码

function returnResult(result){

alert("result is" + result);

}

Java代码

mWebView.loadUrl("javascript:returnResult(" + result + ")");

参考文章

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_42614269/article/details/117497413

智能推荐

php二次编译,编译安装LAMP:PHP篇_许桐楷的博客-程序员宅基地

前面编译安装完成了 Apache 和 MySQL,Apache 的编译安装以及需要的系统之类的见已更新 PHP 7.0.X 版本编译选项——20160115 一、用 root 用户连接上主机二、安装 PHP 需要的组件安装 PHP 需要的大部分必要组件在安装 Apache 之前已经安装了, 现在安装上剩余的一部分。1、安装 libiconv(iconv 模块)libiconv 是由于有些计算机还在...

python脚本常见包_Python 常用包_weixin_39884270的博客-程序员宅基地

转自: http://fsldn.blog.163.com/blog/static/4546432009012111714900/wxPython如果你之前是 windows 程序员,用 MFC 或者 WIN32API 开发界面程序,那进入Python 国度最好的 GUI 选择应该是 wxPython。它是 wxWidgets 的 Python Bind,与 wxWidgets的开发完美同步,最为...

奔驰柏林之声旋转高音喇叭音响 成都蔚一车改_chengshanpa8283的博客-程序员宅基地

适配车型:奔驰S级新E级C级GLC均可升级改装原厂柏林之声音响套装 柏林之声:奔驰原厂柏林之声被誉为音响界的劳斯莱斯,无损安装不并线不破线,可恢复出厂状态,不影响质保,旋转高音2个柏林高音2个柏林中音7个柏林低音2个喇叭罩8个,13件套喇叭套装,增加功放音质提升7倍。 成都蔚...

程序员为什么不在朋友圈炫富?_zhuaizhuaihenguai的博客-程序员宅基地

1、程序员天天加班,没时间炫富:        这就扎铁了,老心,程序员出了名的加班,甚至有程序员调侃加班要随身携带听诊器,一旦发现身体不舒服,及时ctrl+S。。。2、程序员不富:        有钱人在敲代码这方面是不可能的,这辈子都不可能的,大部分码农都是缺钱的,所以没有资本炫富。别看程序员工资高,即使月薪四五万,在帝都几年也凑不到首付,哪有心思去炫富啊。3、程序员炫富没人懂:       ...

番茄时间管理法(Pomodoro Technique):一个番茄是如何让你工作更有效率的_King-Blog的博客-程序员宅基地_pomodoro technique

如果你经常读一些关于提高工作效率或时间管理类的博客,一定听说过番茄时间管理法(Pomodoro Technique)。这是一种极好的帮助你集中注意力、获得更高工作效率的方法。基本上,它的实施方法是这样的:1. 确定你想要做什么(例如:翻译一篇外文)。2. 设定一个25分钟的定时器。3. 工作,直到定时器时间到:这就是一个“番茄钟”。4. 休息5分钟,继续下一

duilib各种布局的作用,相对布局与绝对布局的的意义与用法_dkopg24406的博客-程序员宅基地

我使用duilib快3个月了,总体感觉duilib的使用还是较为简单的,只是刚入门时可能有些摸不清头脑。今天写一篇关于duilib的入门日志,大致说一下duilib中的各个布局的作用,以及很关键的相对布局与绝对布局的意义与用法。希望可以帮到使用duilib的新手朋友们。duilib高手就可以直接省略这篇文章了! 我刚使用duilib的时候非常依赖duilib自带的设计器,用...

随便推点

.NET ToString() format格式化字符串(常用)_女汉纸一枚的博客-程序员宅基地

前言    我们平常会用到货币数据类型,尤其当我们计算金钱或者算数的时候经常会遇到保留几位小数,而且碰到日期格式问题的时候,经常不知道选择什么样的格式比较合适,下面我找了一部分常用的.NET ToString() format格式化字符串分享给大家。内容             C

118_容器_自定义泛型_深入2_无多态_通配符_无泛型数组_jdk7泛型使用_werdzs的博客-程序员宅基地

通配符通配符的测试-TestWildcardpackage genericity_wildcard;import java.util.ArrayList;import java.util.List;/** * ? -->通配符,类型不确定,用于声明变量|形参上 * 不能用在 * 1、创建对象 * 2、创建泛型类 泛型方法 泛型接口上 */public class TestWildca

Android 获取图片exif信息_weixin_33980459的博客-程序员宅基地

使用android api读取图片的exif信息布局代码:&amp;lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot; xmlns:tools=&quot;http://schemas.android.com/tools&quot; android:layout_width=&quot;match_pare...

EAServer 中的pb组件出现内部异常后的补救方法_weixin_34405354的博客-程序员宅基地

2019独角兽企业重金招聘Python工程师标准&gt;&gt;&gt; ...

validateJarFile jar not loaded. See Servlet Spec 2.3, section 9.7.2. Offending c_shuiganhengdaolima的博客-程序员宅基地

原因分析:jbpm4.3自带的jar包与tomcat自带的jar包冲突。               关键涉及两个jar包:                                          servlet-api.jar,jsp-api.jar                这两个jar包tomcat自带的都有。解决办法:把这两个jar