Android WebView和Js交互
现在纯原生Android开发越来越少了,现在一般App都会混合开发,其他混合的技术先不说,最常用就是WebView加载H5页面,再App客户端和Web端交互,提供一些用户信息、客户端Api等,本篇介绍WebView调用Js,Js调用Android方法的知识。
本文使用的Html文件
<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>WebView</title><style type="text/css">body {background: #caff0c;}.btn {line-height: 40px;margin: 10px;background: #cccccc;}#input {margin-left: 5px;}</style></head><body><h2>WebView</h2><div><span>请输入要传递的数据</span><input type="text" id="input"></div><div id="btn"><span class="btn">点我</span></div><div><a href="info://info?name=wally&age=18">点我跳转链接</a></div><script type="text/javascript">//5.按钮点击,调用客户端方法var btnEle = document.getElementById("btn");var inputEle = document.getElementById("input");btnEle.addEventListener("click", function () {var str = inputEle.value;if (window.androidJs) {window.androidJs.setValue(str);} else {alert("window androidJs is not found!")}})//提供给客户端调用的有参Js方法,设置输入文字var remote = function (str) {inputEle.value = str;}//带返回值的Js方法,给客户端获得输入的文字var remoteWithValue = function () {return inputEle.value;}</script></body></html>
JS调用Android方法
(方式一)
1.允许WebView加载Js代码
2.编写一个提供给Web的Js接口类(方法要加上@JavascriptInterface注解)
3.给WebView添加Js接口(将接口对象挂载到Web的window下)
4.Web端调用window下的接口类对象中的Android方法
Android客户端,设置允许加载Js代码,提供Js接口类,注入到Web端的Window中。
/*** 加载的Html位置*/private static final String SETUP_HTML = "file:///android_asset/index.html";//1.允许WebView加载Js代码mWebView.getSettings().setJavaScriptEnabled(true);/*** 2.编写一个提供给Web的Js接口类*/public class JsInterface {private static final String TAG = JsInterface.class.getSimpleName();private Callback mCallback;public JsInterface(Callback callback) {mCallback = callback;}@JavascriptInterfacepublic void setValue(String value) {if (TextUtils.isEmpty(value)) {return;}Log.d(TAG, "value: " + value);if (mCallback != null) {mCallback.onSetValue(value);}}public interface Callback {void onSetValue(String value);}}//3.给WebView添加Js接口mWebView.addJavascriptInterface(new JsInterface(new JsInterface.Callback() {@Overridepublic void onSetValue(final String value) {mMainHandler.post(new Runnable() {@Overridepublic void run() {mResult.setText("value: " + value);}});}}), "androidJs");//4.加载HtmlmWebView.loadUrl(SETUP_HTML);
Html中按钮点击时,Js调用客户端方法
//5.按钮点击,调用客户端方法var btnEle = document.getElementById("btn");var inputEle = document.getElementById("input");btnEle.addEventListener("click", function () {var str = inputEle.value;//判断是否在客户端环境if (window.androidJs) {window.androidJs.setValue(str);} else {alert("window androidJs is not found!")}})
(方式二)
-
和客户端协商协议,以该协议加载Url,客户端WebView.setWebViewClient(),复写shouldOverrideUrlLoading()进行拦截,获取行为和参数。
//和前端协商的协议前缀private static final String CALLBACK_SCHEME = "info://info";mWebView.setWebViewClient(new WebViewClient() {@Overridepublic boolean shouldOverrideUrlLoading(WebView view, String url) {//是否拦截Urlboolean isIntercept = url.startsWith(CALLBACK_SCHEME);//处理拦截特定前缀UrlhandleUrlIntercept(url);if (isIntercept) {return true;} else {return super.shouldOverrideUrlLoading(view, url);}}});/*** 处理拦截特定前缀Url*/private void handleUrlIntercept(String text) {HashMap<String, String> paramsMap = new HashMap<>(5);String result = text.replaceFirst(CALLBACK_SCHEME.concat("\\?"), "");String[] paramsGroup = result.split("&");//按组拆分bold=falsefor (String groupStr : paramsGroup) {String[] paramsKeyValue = groupStr.split("=");//参数和值拆分String paramsName = paramsKeyValue[0];String paramsValue = paramsKeyValue[1];paramsMap.put(paramsName, paramsValue);}final String name = paramsMap.get("name");final String age = paramsMap.get("age");toast("拦截Url,获取参数 -> " + "name:" + name + " age:" + age);}
Web端,超链接加载自定义协议,客户端进行拦截
//点击超链接,加载自定义协议,客户端进行拦截<div><a href="info://info?name=wally&age=18">点我跳转链接</a></div>
(方式三)
使用prompt,发送数据,客户端webView.setWebChromeClient,复写onJsPrompt进行拦截,获取数据。
mWebView.setWebChromeClient(new WebChromeClient() {@Overridepublic boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {//拦截掉,回调4.4一下的Js返回值处理OnMethodResultCallback callback = mCompatKitkatJsMethodCallbackMap.get(url);if (callback != null) {callback.onMethodResult(message);mCompatKitkatJsMethodCallbackMap.remove(url);//拦截后必须取消掉,不发送到客户端WebView,不调用也不行,会阻塞等待结果result.cancel();return true;} else {return super.onJsPrompt(view, url, message, defaultValue, result);}}});
Android调用Js方法
/*** 调用JS方法获取返回值的监听*/interface OnMethodResultCallback {/*** 返回值返回时回调** @param result 函数返回值*/void onMethodResult(String result);}
调用无返回值Js方法
1.Web提供Js方法
2.Android客户端调用
WebView.loadUrl(“javascript:methodName(params)”);,调用Js方法。
客户端,设置发送文字调用Js方法,设置输入内容
//调用无返回值的Js方法mSend.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String str = mInput.getText().toString();mWebView.loadUrl("javascript:if(window.remote){window.remote('" + str + "')}");}});
Web端提供的Js方法,设置输入内容
//提供给客户端调用,设置输入文字var remote = function (str) {inputEle.value = str;}
调用有参Js方法
1.4.4以上,调用webView.evaluateJavascript(),提供一个ValueCallback获取返回值。
2.4.4一下,没有获取返回值的Api,只能使用alert,prompt,confirm。在mWebView.setWebChromeClient()中复写onJsAlert()、onJsConfirm()、onJsPrompt()拦截。
//调动有返回值的Js方法mCallJsParamsMethod.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {final OnMethodResultCallback onCall = new OnMethodResultCallback() {@Overridepublic void onMethodResult(String result) {toast("收到调用的Js方法返回值: " + result);}};if (isOvertopKitkat()) {//4.4以上可以直接用mWebView.evaluateJavascript("javascript:remoteWithValue();", new ValueCallback<String>() {@Overridepublic void onReceiveValue(String value) {onCall.onMethodResult(value);}});} else {mCompatKitkatJsMethodCallbackMap.put(SETUP_HTML, onCall);//4.4以下,使用prompt将信息带出去,受到信息再拦截掉mWebView.loadUrl("javascript:prompt(remoteWithValue());");}}});mWebView.setWebChromeClient(new WebChromeClient() {@Overridepublic boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {//拦截掉,回调4.4一下的Js返回值处理OnMethodResultCallback callback = mCompatKitkatJsMethodCallbackMap.get(url);if (callback != null) {callback.onMethodResult(message);mCompatKitkatJsMethodCallbackMap.remove(url);//拦截后必须取消掉,不发送到客户端WebView,不调用也不行,会阻塞等待结果result.cancel();return true;} else {return super.onJsPrompt(view, url, message, defaultValue, result);}}});/*** 版本号是否大于等于4.4*/protected boolean isOvertopKitkat() {return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;}
欢迎关注我的公众号,一起进步!
本文分享自微信公众号 - Android架构师成长之路(gh_07f996f00d9b)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
