# 交互方式总结
Android (opens new window) 与js (opens new window) 通过 WebView 互相调用方法,实际上是:
Android 去调用 JS 的代码
JS 去调用 Android 的代码
二者沟通的桥梁是 WebView
对于 android (opens new window) 调用 JS 代码的方法有 2 种: (必须开启一个线程调用:webview.post(Runnable))
- 通过
WebView
的loadUrl
(),
比如 web.looadUrl("youhtml:onJsMethod(params)")
;
- 通过
WebView
的evaluateJavascript
();
比如:webview.evaluateJavascript(" youhtml:onJsMethod(params)",new ValueCallback)
对于 JS 调用 Android 代码的方法有 3 种:
- 通过 WebView 的
addJavascriptInterface
(new 对象,对象别名)进行对象映射 ,
js 端通过传过去的对象别名操作安卓端的映射对象里面的方法
通过 WebViewClient 的
shouldOverrideUrlLoading
()方法回调拦截 url通过
WebChromeClient
的onJsAlert()、 onJsConfirm()、onJsPrompt()
方法回调拦截 JS 对话框alert()、confirm() 、prompt()
消息
# 具体分析
# Android 通过 WebView 调用 JS 代码
对于 Android 调用 JS 代码的方法有 2 种:
通过 WebView 的 loadUrl()
通过 WebView 的 evaluateJavascript()
# 通过 WebView 的 loadUrl()
实例介绍:点击 Android 按钮,即调用 WebView JS(文本名为 javascript)中 callJS()
具体使用:
- 步骤 1:将需要调用的 JS 代码以.html 格式放到 src/main/assets 文件夹里
为了方便展示,本文是采用 Andorid 调用本地 JS 代码说明;
实际情况时, Android 更多的是调用远程 JS 代码,即将加载的 JS 代码路径改成 url 即可
需要加载 JS 代码:JavaScript (opens new window) .html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf\-8" />
<title>Carson_Ho</title>
// JS 代码
<script>
// Android 需要调用的方法
function callJS() {
alert("Android 调用了 JS 的 callJS 方法 ");
}
</script>
</head>
</html>
- 步骤 2:在 Android 里通过 WebView 设置调用 JS 代码
Android 代码:MainActivity. Java (opens new window)
注释已经非常清楚
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(
new View.OnClickListener() {
@Override
public void onClick(View v) {
// 必须另开线程进行 JS 方法调用(否则无法调用)
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 后才可使用
- 具体使用
// 只需要将第一种方法的 loadUrl() 换成下面该方法即可
mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此处为 js 返回的结果
}
});
上面实现了使用 evaluateJavascript
的方式,得到 js 的返回值。那么使用 loadURL 如何得到 Js 的返回值呢?前面介绍的仅仅只是针对 loadUrl
调用 alert 的情况。如果要使用 loadURL
的话,则要使用对象映射的方式得到返回值:
安卓端:
网页加载完之后,java 调用 js 方法(注意要在 webview.post(Runnable 中哦)) webView.loadUrl("javascript:shareToFriends()");
添加对象映射、其中的 androidShare 是对象的别名要传递给 js 的,js 使用它来调用 android 的代码
webView.addJavascriptInterface(new JsToJava(), " androidShare");
private class JsToJava{ //这里需要加@JavascriptInterface,4.2 之后提供给 javascript 调用的函数必须带@JavascriptInterface
@JavascriptInterface public void jsMethod(String paramFromJS){
System.out.println("js 返回结果:" + paramFromJS);
//处理返回的结果
}
}
网页端:
<script type="text/javascript">
function shareToFriends() {
var result =
"title:'大朴优选:发现光芒之物',desc:'大朴优选:发现光芒之物',link:'http://m.dapu.com/mgallery\-promotion\- 298.html',imgUrl:'http://activity.dapuimg.com/dapu\_20160825000\_r1\_c1.jpg'";
window.androidShare.jsMethod(result);
//js 端使用安卓端传递过来的 androidShare 对象,来调用安卓端的方法,并将返回的数据 result 传递给安卓端,从而实现了 JS 和安卓之间的互相的传值。 }
</script>
# load 和 evaluateJavascript 方法对比
方式对比图
# 使用建议
两种方法混合使用,即 Android 4.4 以下使用方法 1,Android 4.4 以上方法 2
// Android 版本变量
final int version = Build.VERSION.SDK_iNT;
// 因为该方法在 Android 4.4 版本才可使用,所以使用时需进行版本判断
if (version < 18 ) {
mWebView.loadUrl( "javascript:callJS()");
} else {
mWebView.evaluateJavascript ("javascript:callJS()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此处为 js 返回的结果
}
}
);
}
# JS 通过 WebView 调用 Android 代码
对于 JS 调用 Android 代码的方法有 3 种:
通过
WebView
的addJavascriptInterface()
进行对象映射 ,添加的时候不要在 webview.post 中添加,在主线程中添加通过
WebViewClient
的shouldOverrideUrlLoading
方法回调拦截 url通过
WebChromeClient
的onJsAlert()、onJsConfirm()、 onJsPrompt()
方法回调拦截 JS 对话框 alert()、confirm()、 prompt()消息
# 通过 WebView 的 addJavascriptInterface()进行对象映射
定义一个与 JS 对象映射关系的 Android 类:AndroidtoJs
// 继承自 Object 类
public class AndroidtoJs extends Object {
// 定义 JS 需要调用的方法
// 被 JS 调用的方法必须加入@JavascriptInterface 注解
@JavascriptInterface
public void hello(String msg) {
Toast.make(MainActivity.this, "JS 调用了 Android 的 hello 方法并且传递过来的数据是 "+msg,Toast.lONG).show();
}
}
将需要调用的 JS 代码以.html 格式放到 src/main/assets 文件夹里
需要加载 JS 代码:javascript (opens new window) .html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf\-8" />
<title>Carson</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>
在 Android 里通过 WebView 设置 Android 类与 JS 代码的映射
public class MainActivity extends AppCompatActivity {
WebView mWebView;
@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 对象
//参数 1 :Javascript 对象名
//参数 2 :Java 对象名
mWebView.addJavascriptInterface( new AndroidtoJs(), "test"); //AndroidtoJS 类对象映射到 js 的 test 对象
// 加载 JS 代码
// 格式规定为:file:///android_asset/ 文件名.html
mWebView.loadUrl( "file:///android_asset/javascript.html");
效果图
特点
优点:使用简单,仅将 Android 对象和 JS 对象映射即可
缺点:存在严重的漏洞问题,具体请看文章: 你不知道的 Android WebView 使用漏洞 (opens new window)
# 通过 WebViewClient 的方法 shouldOverrideUrlLoading ()回调拦截 url
- 具体原理:
Android 通过 WebViewClient 的回调方法
shouldOverrideUrlLoading
() 拦截 url解析该 url 的协议
如果检测到是预先约定好的协议,就调用相应方法
- 具体使用:
在 JS 约定所需要的 Url 协议
<!DOCTYPE html>
<html>
<head>
<meta charset="utf\-8" />
<title>Carson_Ho</title>
<script>
function callAndroid() {
//约定的 url 协议为:js://webview?arg1=111&arg2=222\*/
document.location = "js://webview?arg1=111&arg2=222";
}
</script>
</head>
<!-- 点击按钮则调用 callAndroid ()方法 -->
<body>
<button type="button" id="button1" onclick="callAndroid()">
点击调用 Android 代码
</button>
</body>
</html>
当该 JS 通过 Android 的 mWebView.loadUrl("file:///android_asset/javascript.html")
加载后,就会回调 shouldOverrideUrlLoading
(),
接下来继续看步骤 2 :
在 Android 通过 WebViewClient 复写 shouldOverrideUrlLoading ()
//MainActivity.java
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);
// 步骤 1 :加载 JS 代码
// 格式规定为:file:///android_asset/ 文件名.html
mWebView.loadUrl( "file:///android_asset/javascript.html");
// 复写 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();
String value="";
for (String str:collection){
value=value+"键:"+str+"值:"+uri.getQueryParameter(str);
params.put(str,uri.getQueryParameter(str));
}
}
return true ;
}
return super .shouldOverrideUrlLoading(view, url);
}
}
);
}
效果图
特点
优点:不存在方式 1 的漏洞;
缺点: JS 获取 Android 方法的返回值复杂。
如果 JS 想要得到 Android 方法的返回值,只能通过 WebView 的 loadUrl()
去执行 JS 方法把返回值传递回去,相关的代码如下:
// Android:MainActivity.java
mWebView.loadUrl("javascript:returnResult(" + result + ")");
// JS:javascript.html
function returnResult(result) {
alert( "result is" + result);
}
# 通过 WebChromeClient 的 onJsAlert() 、onJsConfirm()、onJsPrompt ()方法回调拦截 JS 对话框 alert()、confirm()、 prompt()消息
在 JS 中,有三个常用的对话框方法:
方式 3 的原理:Android 通过 WebChromeClient 的 onJsAlert()、onJsConfirm() 、onJsPrompt()方法回调分别拦截 JS 对话框(即上述三个方法),得到他们的消息内容,然后解析即可。
下面的例子将用拦截 JS 的输入框(即 prompt()方法) 说明 :
常用的拦截是:拦截 JS 的输入框(即 prompt() 方法)
因为只有 prompt() 可以返回任意类型的值,操作最全面方便、更加灵活;而 alert ()对话框没有返回值;confirm ()对话框只能返回两种状态(确定 / 取消)两个值
步骤 1:加载 JS 代码,如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf\-8" />
<title>Carson_Ho</title>
<script>
function clickprompt() {
// 调用 prompt()
var result = prompt("js://demo?arg1=111&arg2=222");
alert("demo " + result);
}
</script>
</head>
<!-- 点击按钮则调用 clickprompt() -->
<body>
<button type="button" id="button1" onclick="clickprompt()">
点击调用 Android 代码
</button>
</body>
</html>
当使用 mWebView.loadUrl("file:///android_asset/javascript.html")加载了上述 JS 代码后,就会触发回调 onJsPrompt (),具体如下:
如果是拦截警告框(即 alert()),则触发回调 onJsAlert() ;
如果是拦截确认框(即 confirm()),则触发回调 onJsConfirm() ;
在 Android 通过 WebChromeClient 复写 onJsPrompt()
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");
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("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);
}
}
);
}
}
效果图
4、 三种方式的对比 & 使用场景
- 本文链接: https://mrgaogang.github.io/interview/webview-js.html
- 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 许可协议。转载请注明出处!
← 推荐好文 设计一个图片上传系统的大致思路 →