WebView调用Android代码
做好战斗准备,其实也不复杂
情景再现:
运营的H5页面上有个按钮,叫“立即参加”,当用户点击按钮的时候,App上要弹一个吐司出来。
一、通过WebView的addJavascriptInterface ()方式进行映射
1.准备工作
定义我们将来要响应js代码的类
/**
* 与h5交互的共同类
*/
public class ForJs {
//添加注解,不添加注解方法不能够被js调用
//注意方法名称,提供给js的时候也一定要匹配
@JavascriptInterface
public void fromAndroid(){
//我们具体要执行的逻辑,本例只是弹一个吐司
ToastUtil.showToast("这个是android里的方法");
}
// 同上,只是一个有参,一个无参而已
@JavascriptInterface
public void fromAndroid(String msg){
//我们具体要执行的逻辑,本例只是弹一个吐司
ToastUtil.showToast("msg");
}
}
至此,准备工作就已经结束。
2.使用
- android端代码
//如果只需要让js调用android代码,这几行就够了
WebSettings settings = mWebView.getSettings();
settings.setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new ForJs(), "testMall");
参数说明:
1、我们定义的与H5交互的java类
2、java类在H5中映射的对象名称
H5中的js在调用我们的方法的时候,必须知道的是:
映射对象的名称,即示例中的testMall
映射对象提供的方法名称,即示例中的ForJs类中的fromAndroid()
H5中的关键代码
注意这部分代码并不需要我们Android端人员编写,但是却需要我们Android端人员的配合
//js中的点击方法调用,调用的是Android端的无参方法
<div onclick="test()" ></div>
<script>
//js中方法的定义
function test(){
testMall.fromAndroid()//注意 testMall 对象,以及fromAndroid()方法名称
}
</script>
重复一下注意事项:
Androdi端提供给js调用的方法名称要准确无误的告诉前端人员
Js中映射的Java对象名称,要准确无误的告诉Android端人员
一定要看
这种使用方式看起来很简单,但是却有一个致命漏洞:
https://www.jianshu.com/p/3a345d27cd42
二、通过WebViewClinet的shouldOverrideUrlLoading()方法拦截url
- 拦截该Url
- 解析Url协议()//新的API可以直接获取到uri
- 按照约定调用Android端的方法
1.Android端代码
mWebView.loadUrl("file:///android_asset/test.html");
WebSettings settings = mWebView.getSettings();
settings.setJavaScriptEnabled(true);
mWebView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
//请求方式
String method = request.getMethod();
//请求头
Map<String, String> requestHeaders = request.getRequestHeaders();
//uri,给里面有各种我们需要的东西,只需要get即可
Uri uri = request.getUrl();
String host = uri.getHost();
String authority = uri.getAuthority();
// TODO: 2018/12/15 判断,如果符合约定,即执行Android提供好的方法,最后记得return true
//模拟微信封杀抖音链接
if (authority.contains("douyin")){
ToastUtil.showToast("不要看抖音,微信不够丰富吗");
return true;
}
return super.shouldOverrideUrlLoading(view, request);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//如果使用这个方法,那么需要自己手动获取uri,其实与上面并无本质区别
Uri uri = Uri.parse(url);
return super.shouldOverrideUrlLoading(view, url);
}
});
至此,Android端代码完成
2.H5端代码
注意这部分代码并不需要我们Android端人员编写,但是却需要我们Android端人员与H5端人员协商好协议约定
//js中的点击方法调用
<div onclick="test()" ></div>
<script>
//js中方法的定义
function test(){
document.location="https://www.douyin.com/"
}
</script>
与第一种方法比起来,这种写法双方写起来都比较随意。除了Url的协议约定外,别的没有需要注意事项。尤其H5端,其实只是重定向了一个网页而已。
换句话说,在本示例中,如果我们直接在webView中加载https://www.douyin.com/,也会触发我们的Android中的方法,即拦截逻辑。
更重要的是,第二种方式没有第一种方式那种致命漏洞。即利用shouldOverrideUrlLoading方法不存在漏洞
缺点:难以获得返回值,如果想要获得返回值,那么就利用Android端调用Js的方法webView的loadUrl(restul),将Android端的返回值result当做参数传递给js。
Android端示例代码:
WebSettings settings = mWebView.getSettings();
settings.setJavaScriptEnabled(true);
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
Uri uri = request.getUrl();
String authority = uri.getAuthority();
//模拟代码
if (authority.contains("douyin")) {
String result = "这是Android端执行完成之后的结果";
//获取android端的结果后调用js中的方法,把result当参数传递给js
mWebView.loadUrl("javascript:callJsFromAndroid("+result+")");
return true;
}
return super.shouldOverrideUrlLoading(view, request);
}
});
//注意,本示例之所以加此行代码,是为了能弹出js框,具体要看实际需求
mWebView.setWebChromeClient(new WebChromeClient());
mWebView.loadUrl("file:///android_asset/test.html");
H5端示例代码
<script>
//result是android端的处理结果
function callJsFromAndroid(result){
alert(result)
}
</script>
三、通过WebChromeClient的alert()、prompt()、confirm()方法回调js对应的对话框
我在https://www.jianshu.com/p/22265f5c5d78中详细介绍了Android端响应这三种js弹框的方式,如果不熟悉的请先了解一下。一般来将,我们使用的最多的是prompt()方法,因为这个有任意类型的返回值,请看示例代码
Android端代码
WebSettings settings = mWebView.getSettings();
settings.setJavaScriptEnabled(true);
//本行代码添加只是为了让网页在app内打开
mWebView.setWebViewClient(new WebViewClient());
mWebView.setWebChromeClient(new WebChromeClient(){
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
//message即是js弹框中的内容
//解析message,按照约定处理逻辑
if (TextUtils.equals(message,"douyin")) {
// android端的处理逻辑
ToastUtil.showToast("你们怎么都那么好看又有钱");
return true;
}
return super.onJsPrompt(view, url, message, defaultValue, result);
}
});
mWebView.loadUrl("file:///android_asset/test.html");
H5端示例代码
//js中的点击方法调用
<div onclick="test()" ></div>
<script>
function test(){
prompt("douyin")
}
</script>
三种使用方式的对比
1.比较简单,但存在致命漏洞
2.需要双方约定协议,但不存在漏洞
3.与方式2很相似,只是拦截对象不同,也不存在漏洞
写在最后
这些交互方式各有特点,而且并不难以理解,个人认为都应该掌握。