技术标签: 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代码
LYJfunction 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 + ")");
参考文章
前面编译安装完成了 Apache 和 MySQL,Apache 的编译安装以及需要的系统之类的见已更新 PHP 7.0.X 版本编译选项——20160115 一、用 root 用户连接上主机二、安装 PHP 需要的组件安装 PHP 需要的大部分必要组件在安装 Apache 之前已经安装了, 现在安装上剩余的一部分。1、安装 libiconv(iconv 模块)libiconv 是由于有些计算机还在...
转自: http://fsldn.blog.163.com/blog/static/4546432009012111714900/wxPython如果你之前是 windows 程序员,用 MFC 或者 WIN32API 开发界面程序,那进入Python 国度最好的 GUI 选择应该是 wxPython。它是 wxWidgets 的 Python Bind,与 wxWidgets的开发完美同步,最为...
适配车型:奔驰S级新E级C级GLC均可升级改装原厂柏林之声音响套装 柏林之声:奔驰原厂柏林之声被誉为音响界的劳斯莱斯,无损安装不并线不破线,可恢复出厂状态,不影响质保,旋转高音2个柏林高音2个柏林中音7个柏林低音2个喇叭罩8个,13件套喇叭套装,增加功放音质提升7倍。 成都蔚...
1、程序员天天加班,没时间炫富: 这就扎铁了,老心,程序员出了名的加班,甚至有程序员调侃加班要随身携带听诊器,一旦发现身体不舒服,及时ctrl+S。。。2、程序员不富: 有钱人在敲代码这方面是不可能的,这辈子都不可能的,大部分码农都是缺钱的,所以没有资本炫富。别看程序员工资高,即使月薪四五万,在帝都几年也凑不到首付,哪有心思去炫富啊。3、程序员炫富没人懂: ...
如果你经常读一些关于提高工作效率或时间管理类的博客,一定听说过番茄时间管理法(Pomodoro Technique)。这是一种极好的帮助你集中注意力、获得更高工作效率的方法。基本上,它的实施方法是这样的:1. 确定你想要做什么(例如:翻译一篇外文)。2. 设定一个25分钟的定时器。3. 工作,直到定时器时间到:这就是一个“番茄钟”。4. 休息5分钟,继续下一
我使用duilib快3个月了,总体感觉duilib的使用还是较为简单的,只是刚入门时可能有些摸不清头脑。今天写一篇关于duilib的入门日志,大致说一下duilib中的各个布局的作用,以及很关键的相对布局与绝对布局的意义与用法。希望可以帮到使用duilib的新手朋友们。duilib高手就可以直接省略这篇文章了! 我刚使用duilib的时候非常依赖duilib自带的设计器,用...
sublime3 javascript代码自动提示插件及安装
前言 我们平常会用到货币数据类型,尤其当我们计算金钱或者算数的时候经常会遇到保留几位小数,而且碰到日期格式问题的时候,经常不知道选择什么样的格式比较合适,下面我找了一部分常用的.NET ToString() format格式化字符串分享给大家。内容 C
通配符通配符的测试-TestWildcardpackage genericity_wildcard;import java.util.ArrayList;import java.util.List;/** * ? -->通配符,类型不确定,用于声明变量|形参上 * 不能用在 * 1、创建对象 * 2、创建泛型类 泛型方法 泛型接口上 */public class TestWildca
使用android api读取图片的exif信息布局代码:&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_pare...
2019独角兽企业重金招聘Python工程师标准>>> ...
原因分析:jbpm4.3自带的jar包与tomcat自带的jar包冲突。 关键涉及两个jar包: servlet-api.jar,jsp-api.jar 这两个jar包tomcat自带的都有。解决办法:把这两个jar