前言:以下是webView和JS互相调用的基础介绍。
利用WebView调用网页上的JavaScript代码(java调用JS)
说白了,只有一步,就是loadUrl,在WebView中调用Js的基本格式为:
阅读:JSBridge
前言:以下是webView和JS互相调用的基础介绍。
说白了,只有一步,就是loadUrl,在WebView中调用Js的基本格式为:
webView.loadUrl("javascript:methodName(parameterValues)");
代码示例,现有以下这段JavaScript代码,将展示3种用法:
<script type="text/javascript">
function readyToGo() {
alert("Hello")
}
function alertMessage(message) {
alert(message)
}
function getYourCar(){
return "Car";
}
</script>
注意:需要在主线程中发起调用
String call = "javascript:readyToGo()";
webView.loadUrl(call);
String call = "javascript:alertMessage(\"" + "content" + "\")";
webView.loadUrl(call);
@TargetApi(Build.VERSION_CODES.KITKAT)
private void evaluateJavaScript(WebView webView){
webView.evaluateJavascript("getYourCar()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
Log.d("findCar",s);
}
});
}
从API19开始,Android提供了@JavascriptInterface对象注解的方式来建立起Javascript对象和Android原生对象的绑定,提供给JavScript调用的函数必须带有@JavascriptInterface。
实际上,就是3个步骤:
1,@JavascriptInterface注解;
2,注册JavaScriptInterface;
3,调用,如window.android.show("JavaScript called~!");
@JavascriptInterface
public void show(String s){
Toast.makeText(getApplication(), s, Toast.LENGTH_SHORT).show();
}
2.注册JavaScriptInterface
webView.addJavascriptInterface(this, "android");
addJavascriptInterface的作用是把this所代表的类映射为JavaScript中的android对象。
3.编写JavaScript代码
function toastClick(){
window.android.show("JavaScript called~!");
}
1.定义一个带返回值的Java方法,并使用@JavaInterface:
@JavaInterface
public String getMessage(){
return "Hello,boy~";
}
2.添加JavaScript的映射
webView.addJavaScriptInterface(this,"Android");
3.通过JavaScript调用Java方法
function showHello(){
var str=window.Android.getMessage();
console.log(str);
}
【参考:】
使用 WebView 加载网页,有时候需要进行js交互,相互传递数据和响应事件。
android 调用 js 代码:
WebView#loadUrl("javascript:func('" + arg + "')")
WebView#evaluateJavascript(String script, @Nullable ValueCallback<String> resultCallback)
js 调用 android 代码:
WebView#addJavascriptInterface(Object object, String name)
进行对象映射WebViewClient#shouldOverrideUrlLoading()
来拦截Url调用代码WebChromeClient
的 onJsAlert()
、onJsConfirm()
、onJsPrompt()
拦截 js 中的对话框 alert() / confirm() / prompt()基础设置
var settings = webView.settings
// 支持js交互
settings.javaScriptEnabled = true
// 允许js弹窗
settings.javaScriptCanOpenWindowsAutomatically = true
// 使用dom存储,如果加载的是本地 assets 的 HTML,最好设为 false,否则可能会出现第一次加载空白
settings.domStorageEnabled = true
settings.cacheMode = WebSettings.LOAD_NO_CACHE
settings.allowFileAccess = true
settings.useWideViewPort = true
settings.setSupportZoom(false) // 支持缩放
settings.defaultTextEncodingName = "utf-8"
settings.loadWithOverviewMode = true
settings.setNeedInitialFocus(false)
settings.userAgentString = settings.userAgentString + "; Android"
/*解决图片不显示*/
settings.blockNetworkImage = false //解决图片不显示
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//允许混合(http,https)
settings.mixedContentMode = WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE
}
Android 调用 js 方法
// 不带参数
webView.loadUrl("javascript:postShareParams()")
// 带参数,常用参数 字符串(String),JSONArray 字符串(.toString()),JSONObject字符串(.toString())
var data:JSONArray = ...
// 要把字符串里的单引号转义一下(如果没转义的话)
var json: String = data.toString().replace("'", "\\\'")
// 防止这里成对的单引号,导致报错
webView.loadUrl("javascript:setTreeData('${json}')")
// 也可以是一段 js 代码,下面的代码即是暂停页面中的视频播放
webView.loadUrl("javascript:(function() { var videos = document.getElementsByTagName('video'); for(var i=0;i<videos.length;i++){videos[i].pause();}})()")
// 改变 span 的文字
weView.loadUrl("javascript:$('#broadcast').find('span').text('暂停')")
// 隐藏视图
webView.loadUrl("javascript:$('#info_delete').hide()")
如果调用 js 方法有传参,参数是一个 JSON 字符串( JSONArray 字符串(.toString())或 JSONObject字符串(.toString())),js 端需要使用 eval('(' + result + ')')
或者 "JSON.parse(result)"
将这个JSON 字符串解析为对象。
function setTreeData(result) {
alert(result);
var data = eval('(' + result + ')');
//var data = JSON.parse(result);
//alert(JSON.stringify(data));
// reloadChart(data)
}
使用 eval() 解析 json,容错能力较好,这种情况
[{}, {}, ]
依然能正常解析,而JSON.parse()
则报错。
eval 的用法可参考:js中eval()的使用说明:https://www.cnblogs.com/firstlady/p/11347382.html
注意,android端不能直接传对象(会被转成地址,或对象重写的toString())给 js,但是可以通过上述方法将 JSON 字符串转成对象;android 端也不能直接接收对象,但是js可以通过
JSON.stringify(data)
将对象转成一个 JSON 字符串再传给 android。
先来说说使用这个方法的优点:
使用这个方法不会刷新页面,如果使用第一种方法则会刷新页面
*注意:这个方法只能在Android4.4之后使用
使用方式:
1 .将minSdkVersion最低版本改为19 (build.gradle----minSdkVersion)
2. 直接替 loadUrl
方式
androidWeb.evaluateJavascript("javascript:clickJS()",object : ValueCallback<String>{
override fun onReceiveValue(value: String?) {
// 这里返回JS的结果
}
})
两种方式的区别:
loadUrl()
evaluateJavascript()
根据情况使用两种方式,我们可以根据当前项目开发的需求选择相应的使用方式,我们可以直接判断版本号来区分使用方式:
if (Build.VERSION.SDK_INT< 18) {
android_web.loadUrl("javascript:clickJS()")
} else {
android_web.evaluateJavascript("javascript:clickJS()"), {
//返回JS方法中的返回值,我们没有写返回值所以为null
}
}
webView.addJavascriptInterface(AndroidApi(), "AndroidApi")
inner class AndroidApi {
// 有返回值
@JavascriptInterface
fun getAppParams(): String {
var params = JSONObject()
Utils.Clipboard.copy(mParams.token)
return params.toString()
}
// 带参数
@JavascriptInterface
fun jumpDetail(data: String?) {
// { code: code, url: data, abstract: abstract }
var dataStr = StringUtils.getNonNullStr(data, "{}")
var type = object : TypeToken<LinkedHashMap<String, String>>() {}.type
var dataMap = Gson().fromJson<LinkedHashMap<String, String?>>(dataStr, type)
// ...
}
}
注意:android 端也不能直接接收对象,但是js可以通过
JSON.stringify(data)
将对象转成一个 JSON 字符串再传给 android。
WebViewClient()
的 shouldOverrideUrlLoading()
方法拦截Url调用 Android 代码<script type="text/javascript">
function clickAndroid(){
//定义url协议
document.location = "js://webview?name=zhangsan&age=20&sex=0"
}
</script>
webView.webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
val url = request?.url.toString()
return shouldOverrideUrlLoading(view, url)
}
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
// 获取Uri 这里的URL是我们在JS方法中写的URL协议"js://webview?name=zhangsan&age=20&sex=0"
val uri = Uri.parse(url)
if (uri.scheme == "js") {
if (uri.authority == "webview") {
val makeText = Toast.makeText(this@MainActivity, url, Toast.LENGTH_LONG)
makeText.setGravity(Gravity.CENTER, 0, 0)
makeText.show()
}
return true
}
view?.loadUrl(url)
return true
}
override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler, error: SslError) {
handler.proceed() //接受所有网站的证书
}
}
WebChromeClient
的 onJsAlert()
、onJsConfirm()
、onJsPrompt()
拦截JS中的对话框alert() / confirm() / prompt()js 代码,注意代码里的 alert(result)
function setTreeData(result) {
alert(result);
var data = eval('(' + result + ')');
//var data = JSON.parse(result);
//alert(JSON.stringify(data));
// reloadChart(data)
}
Kotlin 代码:
webView.webChromeClient = object : WebChromeClient() {
override fun onProgressChanged(view: WebView, newProgress: Int) {
// 进度变化回调,可用于设置进度条进度
}
override fun onReceivedTitle(view: WebView, title: String) {}
override fun onJsAlert(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean {
Utils.Log.i("onJsAlert url=$url; message=$message")
Toast.makeText(this@EventAnalysisActivity, message, Toast.LENGTH_LONG).show()
result!!.confirm()
return super.onJsAlert(view, url, message, result)
}
override fun onJsConfirm(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean {
return super.onJsConfirm(view, url, message, result)
}
override fun onJsPrompt(view: WebView?, url: String?, message: String?, defaultValue: String?, result: JsPromptResult?): Boolean {
return super.onJsPrompt(view, url, message, defaultValue, result)
}
}
这种方式页可以用作 js 的 debug 调试。
addJavascriptInterface()
使用起来方便简洁,但是在 Android 低版本下有问题,用于Android4.4以上。
shouldOverrideUrlLoading()
使用起来没有漏洞,但是两端定协议,使用起来比较麻烦,主要用于不需要返回值的情况。
onJsAlert()
、onJsConfirm()
、onJsPrompt()
拦截 js 中的对话框 alert() / confirm() / prompt() 和第二种方式一样,没有漏洞,而且也复杂,并且需要协议来规定它。
Android中 WebView控件支持JS与本地代码的交互。
// 是否允许在webview中执行javascript
webSettings.setJavaScriptEnabled(true);
//添加JS的接口名称
mWebView.addJavascriptInterface(this, "mobiletojs");
其主要分为两类:
1:JS调用本地代码
通过window.mobiletojs.saveLogin(username, password);
mobiletojs 对应 mWebView.addJavascriptInterface(this, "mobiletojs"); 中的接口名称
2:本地代码调用JS
在Activity中实现方法调用 mWebView.loadUrl("javascript:setUsername('ddd')"); 即可实现对JS:设置用户名方法的调用
//Android 本地代码
package com.whroid.commonapp.ui;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.webkit.WebSettings;
import android.webkit.WebView;
@SuppressLint("JavascriptInterface")
public class WebViewJSUI extends Activity {
public static final String TAG = "WebViewJSUI";
private WebView mWebView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mWebView = new WebView(this);
setContentView(mWebView);
WebSettings webSettings = mWebView.getSettings();
// 是否允许在webview中执行javascript
webSettings.setJavaScriptEnabled(true);
// 为JS添加本地调用的接口 名称
mWebView.addJavascriptInterface(this, "mobiletojs");
// 加载网页
mWebView.loadUrl("file:///android_asset/test_webview_js.html");
}
//页面点击登录时 由页面调用
public void saveLogin(String username,String password)
{
Log.w(TAG, "调用登录:onLoin:"+username+" password:"+password);
}
//由代码调用页面js,实现对页面的编辑
public void setUsername()
{
mWebView.loadUrl("javascript:setUsername('ddd')");
}
public String getUsername()
{
return "来自android 的用户名";
}
public String getPassword()
{
return "mobile password";
}
}
//对应的页面,以及相应的JS
content="HTML Tidy for HTML5 (experimental) for Windows https://github.com/w3c/tidy-html5/tree/c63cc39" />
titlefunction onLogin() {
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
var userAgent = navigator.userAgent;
var index = userAgent.indexOf("Android");
if (index > 0) {
window.mobiletojs.saveLogin(username, password);
}
}
function setUsername(name) {
document.getElementById("username").value = name;
}
function onInit() {
var userAgent = navigator.userAgent;
var index = userAgent.indexOf("Android");
if (index > 0) {
//调用android或者ios本地方法,获取用户名或密码
var username = window.mobiletojs.getUsername();
var password = window.mobiletojs.getPassword();
document.getElementById("username").value = username;
document.getElementById("password").value = password;
}
}
//页面开始加载时,触发
if (document.readyState == "complete") {
onInit()
} else {
document.onreadystatechange = function() {
if (document.readyState == "complete") {
onInit()
}
}
}
test
οnclick="onLogin()" /> |
[android] WebView与Js交互
获取WebView对象 调用WebView对象的getSettings()方法,获取WebSettings对象 调用WebSettings对象的setJavaScriptEnabled()方法,设置j ...
webview与js交互(转)
原文:http://www.cnblogs.com/vanezkw/archive/2012/07/02/2572799.html 对于android初学者应该都了解webView这个组件.之前我也是 ...
Android中使用WebView与JS交互全解析
1.概述首先,需要提出一个概念,那就是hybrid,主要意思就是native原生Android和h5混合开发.为什么要这样做呢?大家可以想象一下针对于同一个活动,如果使用纯native的开发方式,An ...
webview与js交互
对于android初学者应该都了解webView这个组件.之前我也是对其进行了一些简单的了解,但是在一个项目中不得不用webview的时候,发现了webview的强大之处,今天就分享一下使用we ...
android Webview 实现js调用java代码实现Activity跳转
今天有了一个需求,在android里webview加载的html页面,要求点击html页面的按钮实现Activity的跳转. 咱是是菜鸟,webview的接触不多,于是就和度娘来了次亲密接触.在其中也 ...
webView和js交互
与 js 交互 OC 调用 JS // 执行 js - (void)webViewDidFinishLoad:(UIWebView *)webView { NSString *title = [web ...
iOS webView与js交互在文本空格上输入文字
项目要求:webview加载html网址,内容为填空题型文本,需要在横线上添加答案,并点击提交按钮后再将答案进行回显 正常加载的效果图片: 这个是用js交互后的效果图: 点击空格,输入想输入的答案,如 ...
Android原生webview中js交互
http://www.cnblogs.com/android-blogs/p/4891264.html Html页面和Java代码结合的方式一般用在界面经常被更改 的情况下,可以讲html放在网络中, ...
Android WebView与JavaScript交互操作(Demo)
应用场景: 为了使Android移动项目能够在较短的时间内完成开发,同时降低技术人员开发的成本投入,往往会采用Hybrid APP的开发模式.相关Hybrid APP(混合型应用)参看:http:// ...
随机推荐
关于js touch事件 的引用设置
一开始做前端页面的时候,接触的也是js,但是随后便被简单高效的jquery吸引过去,并一直使用至今. 而js,则被我主观的认为底层技术而抛弃. 直到这几天工作需要,研究移动端页面的触屏滑动事件,搜索j ...
(BFS)poj1465-Multiple
题目地址 题意可理解为我们有一些给定的元素,要用它们组成数,如果一个长度(x)所有组成的数都不是给定的另一个数(n)的倍数,并且长度为x的数中有模n的不同于长度小于x的数模n的数,那么继续延长这个数的 ...
Java基础-JVM类加载机制
JVM的类加载是通过ClassLoader及其子类来完成的,类的层次关系和加载顺序可以由下图来描述: 1)Bootstrap ClassLoader /启动类加载器 $JAVA_HOME中jre/li ...
wince下的CPU和内存占用率计算
#include DWORD Caculation_CPU(LPVOID lpVoid) { MEMORYSTATUS MemoryInfo; DWORD Perc ...
分享一款页面视差滚动切换jquery.localscroll插件
今天给大家分享一款页面视差滚动切换jquery.localscroll插件. 滚动鼠标液动条看下页面的切换效果.该插件适用浏览器:IE8.360.FireFox.Chrome.Safari.Opera ...
02线性表链式存储_LinkList--(线性表)
#include "stdio.h" #include "string.h" #include "ctype.h" #include &qu ...
代码-Weka的LinearRegression类
package kit.weka; import weka.classifiers.Evaluation; import weka.classifiers.functions.LinearRegres ...
【排障】每次打开word都提示要安装配置
为什么每次打开word都提示要安装配置?很多人在打开word时,总是提示要安装配置一遍,花去不少时间,这是由于电脑里有两个不同版本的office软件,产生的原因可能是原来的卸载了没卸载干净,或是安装了 ...
数据结构算法及应用&mdash;&mdash;二叉树
一.二叉树性质 特性1 包含n (n> 0 )个元素的二叉树边数为n-1 特性2 二叉树的高度(height)或深度(depth)是指该二叉树的层数(有几层元素,而不是有层的元素间隔) 特性3 ...
最全面总结 Android WebView与 JS 的交互方式
对于Android调用JS代码的方法有2种:
示例演示:点击Android按钮,即调用WebView JS(文本名为javascript)中callJS()
示例说明:为了方便展示,本文是采用Andorid调用本地JS代码说明;实际情况时,Android更多的是调用远程JS代码,即将加载的JS代码路径改成url即可
方式一:通过WebView的loadUrl()
1、新建 javascript.html 放着 src/main/assets 文件夹下(选中 main,右键 new - folder - Assets Folder)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="Generator" content="EditPlus®">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<title>Document</title>
</head>
<body>
<script>
// Android需要调用的方法
function callJS(){
alert("Android调用了JS的callJS方法");
}
</script>
</body>
</html>
2、在Android里通过WebView设置调用JS代码
public class MainActivity extends AppCompatActivity {
WebView mWebView;
Button button;
@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);
// 设置允许JS弹窗
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
// 先载入JS代码
// 格式规定为:file:///android_asset/文件名.html
mWebView.loadUrl("file:///android_asset/javascript.html");
button = (Button) findViewById(R.id.button);
button.setOnClickListener(v -> {
// 通过Handler发送消息
mWebView.post(new Runnable() {
@Override
public void run() {
// 注意调用的JS方法名要对应上
// 调用javascript的callJS()方法
mWebView.loadUrl("javascript:callJS()");
}
});
});
// 由于设置了弹窗检验调用结果,所以需要支持js对话框
// webview只是载体,内容的渲染需要使用webviewChromClient类去实现
// 通过设置WebChromeClient对象处理JavaScript的对话框
//设置响应js 的Alert()函数
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder b = new AlertDialog.Builder(MainActivity.this);
b.setTitle("Alert");
b.setMessage(message);
b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
b.setCancelable(false);
b.create().show();
return true;
}
});
}
}
注意:
JS代码调用一定要在 onPageFinished()
回调之后才能调用,否则不会调用
onPageFinished()
属于WebViewClient类的方法,主要在页面加载结束时调用
方式二:通过WebView的evaluateJavascript()
该方法的执行不会使页面刷新,而第一种方法(loadUrl )的执行则会。比第一种方法效率更高、使用更简洁。Android 4.4 后才可使用
button.setOnClickListener(v -> {
// 通过Handler发送消息
mWebView.post(new Runnable() {
@Override
public void run() {
mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此处为 js 返回的结果
}
});
}
});
});
对于JS调用Android代码的方法有3种:
addJavascriptInterface()
进行对象映射shouldOverrideUrlLoading()
方法回调拦截 urlonJsAlert()
、onJsConfirm()
、onJsPrompt()
方法回调拦截JS对话框alert()
、confirm()
、prompt()
消息演示效果:
方式1:通过 WebView的 addJavascriptInterface() 进行对象映射
1、定义一个与JS对象映射关系的Android类:AndroidtoJs
public class AndroidtoJs extends Object {
// 定义JS需要调用的方法
// 被JS调用的方法必须加入@JavascriptInterface注解
@JavascriptInterface
public void hello(String msg) {
System.out.println("JS调用了Android的hello方法");
}
}
2、将JS代码以 .html 格式放到 src/main/assets 文件夹里
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="Generator" content="EditPlus®">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<title>Document</title>
<script>
function callAndroid(){
// 由于对象映射,所以调用test对象等于调用Android映射的对象
test.hello("js调用了android中的hello方法");
}
</script>
</head>
<body>
//点击按钮则调用callAndroid函数
<button type="button" id="button1" onclick="callAndroid()"></button>
</body>
</html>
3、在Android里通过WebView设置Android类与JS代码的映射
// 通过addJavascriptInterface()将Java对象映射到JS对象
//参数1:Javascript对象名
//参数2:Java对象名
mWebView.addJavascriptInterface(new AndroidtoJs(), "test");//AndroidtoJS类对象映射到js的test对象
此方法存在严重的漏洞问题,具体请看文章:你不知道的 Android WebView 使用漏洞
方式2:通过 WebViewClient 的方法shouldOverrideUrlLoading ()回调拦截 url
Android通过 WebViewClient 的回调方法shouldOverrideUrlLoading()
拦截 url,解析该 url 的协议,如果检测到是预先约定好的协议,就调用相应方法
1、将JS代码以 .html 格式放到 src/main/assets 文件夹里
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="Generator" content="EditPlus®">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<title>Document</title>
<script>
function callAndroid2(){
/*约定的url协议为:js://webview?arg1=111&arg2=222*/
document.location = "js://webview?arg1=111&arg2=222";
}
</script>
</head>
<body>
<button type="button" id="button" value="js调用安卓方式2" onclick="callAndroid2()"></button>
</body>
</html>
2、在Android通过 WebViewClient复写shouldOverrideUrlLoading()
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String 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<String, String> params = new HashMap<>();
Set<String> collection = uri.getQueryParameterNames();
}
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
}
);
该方式不存在方式1的漏洞,但JS获取Android方法的返回值复杂
如果JS想要得到Android方法的返回值,只能通过 WebView 的 loadUrl ()去执行 JS 方法把返回值传递回去,相关的代码如下:
// Android:MainActivity.java
mWebView.loadUrl("javascript:returnResult("+3+")");
// JS:javascript.html
function returnResult(result){
alert("result is" + result);
}
方式3:通过 WebChromeClient 的onJsAlert()
、onJsConfirm()
、onJsPrompt()
方法回调拦截JS对话框alert()
、confirm()
、prompt()
消息
Android通过 WebChromeClient 的onJsAlert()
、onJsConfirm()
、onJsPrompt()
方法回调分别拦截JS对话框
(即上述三个方法),得到他们的消息内容,然后解析即可
下面的例子将用拦截 JS的输入框(即prompt()
方法)。常用的拦截是:拦截 JS的输入框(即prompt()
方法),因为只有prompt()
可以返回任意类型的值,操作最全面方便、更加灵活;而alert()
对话框没有返回值;confirm()
对话框只能返回两种状态(确定 / 取消)两个值
1、javascript 放在 assets 文件夹下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="Generator" content="EditPlus®">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<title>Document</title>
<script>
function callAndroid3(){
// 调用prompt()
var result=prompt("js://webview?arg1=111&arg2=222");
alert("demo " + result);
}
</script>
</head>
<body>
<button type="button" id="button3" onclick="callAndroid3()">js调用安卓方式3</button>
</body>
</html>
当使用mWebView.loadUrl("file:///android_asset/javascript.html")
加载了上述JS代码后,就会触发回调onJsPrompt()
,具体如下:
如果是拦截警告框(即alert()),则触发回调onJsAlert();
如果是拦截确认框(即confirm()),则触发回调onJsConfirm();
2、在Android通过WebChromeClient复写onJsPrompt()
mWebView.setWebChromeClient(new 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<String, String> params = new HashMap<>();
Set<String> collection = uri.getQueryParameterNames();
//参数result:代表消息框的返回值(输入值)
result.confirm("js调用了Android的方法成功啦");
}
return true;
}
return super.onJsPrompt(view, url, message, defaultValue, result);
}
/*// 通过alert()和confirm()拦截的原理相同,此处不作过多讲述
// 拦截JS的警告框
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}*/
// 拦截JS的确认框
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return super.onJsConfirm(view, url, message, result);
}
});
Android与Js交互之JSBridge的使用
Android 中BridgeWebView 简单使用,以及爬坑
Android4.2以下的addJavascriptInterface
存在安全漏洞,虽然在Android4.2之后用@JavascriptInterface
代替了addJavascriptInterface
但是由于兼容性和安全性问题,基本上我们不会再利用Android系统为我们提供的addJavascriptInterface
方法或者@JavascriptInterface
注解来实现,所以我们只能另辟蹊径,去寻找既安全,又能实现兼容Android各个版本的方案
1、项目gradle文件中添加maven { url "https://jitpack.io" }
,位置如下
allprojects {
repositories {
google()
jcenter()
maven { url "https://jitpack.io" }
}
}
2、在module的gradle中添加依赖
dependencies {
...
implementation 'com.github.lzyzsd:jsbridge:1.0.4'
}
3、布局文件中用BridgeWebView代替WebView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<EditText
android:id="@+id/et"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/bt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Android调用js方法" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:text="以下是webview" />
<com.github.lzyzsd.jsbridge.BridgeWebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
4、Activity 中的代码
public class JsBridgeActivity extends AppCompatActivity {
private EditText et;
private Button bt;
private BridgeWebView webview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_jsbridge);
et = (EditText) findViewById(R.id.et);
bt = (Button) findViewById(R.id.bt);
webview = (BridgeWebView) findViewById(R.id.webview);
webview.setDefaultHandler(new DefaultHandler());
webview.setWebChromeClient(new WebChromeClient());
webview.loadUrl("file:///android_asset/test.html");
// 注册监听方法当js中调用callHandler方法时会调用此方法(handlerName必须和js中相同)
webview.registerHandler("submitFromWeb", new BridgeHandler() {
@Override
public void handler(String data, CallBackFunction function) {
Log.e("TAG", "js返回:" + data);
//显示js传递给Android的消息
Toast.makeText(JsBridgeActivity.this, "js返回:" + data, Toast.LENGTH_LONG).show();
//Android返回给JS的消息
function.onCallBack("我是js调用Android返回数据:" + et.getText().toString());
}
});
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 调用js中的方法(必须和js中的handlerName想同)
webview.callHandler("functionInJs", "Android调用js66", new CallBackFunction() {
@Override
public void onCallBack(String data) {
Log.e("TAG", "onCallBack:" + data);
Toast.makeText(JsBridgeActivity.this, data, Toast.LENGTH_LONG).show();
}
});
}
});
}
}
5、html 放在 assets 文件夹中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!-- Compiled and minified CSS -->
<link rel="stylesheet" href="./materialize.min.css">
<!-- Compiled and minified JavaScript -->
<script src="./materialize.min.js"></script>
<title>Test</title>
</head>
<body>
<div class="input-field col s6">
<input placeholder="请输入数据" id="text1" type="text" class="validate">
</div>
<button class="waves-effect waves-light btn" onclick="testClick();">js调用Android方法</button>
</body>
<script>
//js调用Android方法:接收Android传递过来的数据,并做处理
function testClick() {
//参数一:调用java中的方法 submitFromWeb是方法名,必须和Android中注册时候的方法名称保持一致
//参数二:返回给Android端的数据,可以为字符串,json等信息
//参数三:js接收到Android传递过来的数据之后的相应处理逻辑
window.WebViewJavascriptBridge.callHandler(
'submitFromWeb'
, {'param': "JS成功接收到数据---"}
, function(responseData) {
alert(responseData)
}
);
}
//JS注册事件监听
function connectWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) {
callback(WebViewJavascriptBridge)
} else {
document.addEventListener(
'WebViewJavascriptBridgeReady'
, function() {
callback(WebViewJavascriptBridge)
},
false
);
}
}
//注册回调函数,第一次连接时调用 初始化函数
connectWebViewJavascriptBridge(function(bridge) {
//初始化
bridge.init(function(message, responseCallback) {
var data = {
'Javascript Responds': 'Wee!'
};
alert("jasdashjd");
responseCallback(data);
});
//Android调用js方法:functionInJs方法名称需要保持一致 ,并返回给Android通知
bridge.registerHandler("functionInJs", function(data, responseCallback) {
alert(data);
var data2 = document.getElementById("text1").value;
var responseData = "我是Android调用js方法返回的数据---"+ data2;
responseCallback(responseData);
});
})
</script>
</html>
运行效果:
Android使用WebView加载网页选择文件上传
新建 upload.html 放在 assets 下
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,maximun-scale=1,mininum-scale=1,user-scale=1">
<tile>文件选择</tile>
<style>
.filechooser {
width: 95%;
height: 50px;
-webkit-background-clip: border-box;
}
</style>
<script>
var display = function(){
var path = document.getElementById("file_chooser").textContent;
document.getElementById("filechooser_display").innerHTML("<b>"+path+"</b>");
}
</script>
</head>
<body>
<fieldset>
<input class="filechooser" id="file_chooser" type="file" placeholder="选择文件" onchange="display()"
oninput="display()">
<p id="filechooser_display"></p>
</fieldset>
</body>
</html>
Activity
public class UploadActivity extends AppCompatActivity {
private WebView webView;
private ValueCallback<Uri> uploadFile;
private ValueCallback<Uri[]> uploadFiles;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_upload);
webView = findViewById(R.id.webview);
webView.setWebChromeClient(new WebChromeClient() {
// For Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
Log.i("test", "openFileChooser 1");
uploadFile = uploadFile;
openFileChooseProcess();
}
// For Android < 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsgs) {
Log.i("test", "openFileChooser 2");
uploadFile = uploadFile;
openFileChooseProcess();
}
// For Android > 4.1.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
Log.i("test", "openFileChooser 3");
uploadFile = uploadFile;
openFileChooseProcess();
}
// For Android >= 5.0
public boolean onShowFileChooser(WebView webView,
ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams) {
Log.i("test", "openFileChooser 4:" + filePathCallback.toString());
uploadFiles = filePathCallback;
openFileChooseProcess();
return true;
}
});
webView.loadUrl("file:///android_asset/upload.html");
}
private void openFileChooseProcess() {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
startActivityForResult(Intent.createChooser(i, "选择文件"), 0);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case 0:
if (uploadFile != null) {
Uri result = data == null || resultCode != RESULT_OK ? null
: data.getData();
uploadFile.onReceiveValue(result);
uploadFile = null;
}
if (uploadFiles != null) {
Uri result = data == null || resultCode != RESULT_OK ? null
: data.getData();
uploadFiles.onReceiveValue(new Uri[]{result});
uploadFiles = null;
}
break;
default:
break;
}
} else if (resultCode == RESULT_CANCELED) {
if (uploadFile != null) {
uploadFile.onReceiveValue(null);
uploadFile = null;
}
if (uploadFiles != null) {
uploadFiles.onReceiveValue(null);
uploadFiles = null;
}
}
}
@Override
protected void onResume() {
super.onResume();
webView.onResume();
}
@Override
protected void onPause() {
super.onPause();
webView.onPause();
}
/**
* 确保注销配置能够被释放
*/
@Override
protected void onDestroy() {
if (webView != null) {
webView.destroy();
}
super.onDestroy();
}
}
对代码的一些解释:
点击按钮需要模拟form提交的方式去上传文件, 此时会回调 openFileChooser(...)
方法。 然后在这个方法里接收并处理参数ValueCallback <uri> uploadMsg
。里面的 uri
就是所从本地选择的文件路径。然后通过 Intent 的startActivityForResult(…)
方法跳转到系统选择文件的界面进行文件选择。 最后在onActivityResult(int requestCode, int resultCode, Intent data)
方法中获取 data中的字符串路径,转换成Uri
格式,并传给uploadMsg
即可
上面就是4.4 以前的实现过程,5.0及以上的, webkit不再支持openFileChooser(...)
,而是使用onShowFileChooser(…)