目录
示例代码
Demo: https://github.com/gwpp/jsinterface
前言
在《App与Js交互(一)iOS》中我们详细的列举了iOS与JS的各种交互方式,那么Android端的交互又是怎样的呢?下面就来为大家一一介绍。
ps:本人iOS出身,Android学习时间不长,如果有BUG还请在下方评论中及时反馈,感谢非常。
Android系统中的交互
方案一,拦截跳转
-
初始化:
// WebView默认是不支持Android&JS通信的,要在WebView初始化的时候打开这个开关 mWebView.getSettings().setJavaScriptEnabled(true);
-
原生调用JS:
Android调用JS的常规方法有两种,如下:-
方法 A:
mWebView.loadUrl("javascript:alert('1234')");
是的,你没有看错,就是渲染URL的方法,它也可以用来执行js代码,但是弊端就是执行完了这段代码后WebView上原有的内容有可能会被覆盖,并且拿不到JS方法的return值,所以一般不会使用这种方式。
-
方法 B:
String js = "getCookieWithKey('username')"; mWebView.evaluateJavascript(js, new ValueCallback<String>() { @Override public void onReceiveValue(String s) { // 这里可以处理被调用js方法的return showNativeMessage("调用JS方法后得到的返回值是:" + s); } });
这种方法会比方法A好很多,首先不会影响WebView原本渲染的内容,其次它还支持JS方法的返回值,所以在正常开发中更多时候用的是方法B。
-
-
JS调用原生:
用过WebView的同学应该都知道有个东西叫做WebViewClient
,这个东西就可以实现我们的需求——拦截跳转。// JS代码 =========================== // 登录 window.location = 'app://login?account=13011112222&password=123456'; // 登出 window.location = 'app://share?title=分享的标题&desc=分享的描述' // Android代码 ======================= private void setupWebView() { WebViewClient webViewClient = new WebViewClient() { // 老方法 @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { Uri uri = Uri.parse(url); // 如果拦截单的链接是app协议的就说明是我们需要处理的链接 if (uri.getScheme().contentEquals("app")) { callNative(uri); // 返回true就是告诉WebView该链接不需要你处理了,已经被我“消费”了。 return true; } return super.shouldOverrideUrlLoading(view, url); } // 新方法,API21之后支持 @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { if (Build.VERSION.SDK_INT < 21) { return super.shouldOverrideUrlLoading(view, request); } Uri uri = request.getUrl(); if (uri.getScheme().contentEquals("app")) { callNative(uri); return true; } return super.shouldOverrideUrlLoading(view, request); } }; mWebView.setWebViewClient(webViewClient); } /** * js 调用原生方法时的特殊跳转链接 * @param uri 特殊的跳转链接 */ private void callNative(Uri uri){ String host = uri.getHost(); if (host.contentEquals("login")) { String account = uri.getQueryParameter("account"); String password = uri.getQueryParameter("password"); showNativeMessage(String.format("执行登录操作,账号为:%s,密码为:%s", account, password)); } else if (host.contentEquals("share")) { String title = uri.getQueryParameter("title"); String desc = uri.getQueryParameter("desc"); showNativeMessage(String.format("执行分享操作,title为:【%s】,desc为:【%s】", title, desc)); } }
方案二,JavaScriptInterface
-
初始化:
// WebView默认是不支持Android&JS通信的,要在WebView初始化的时候打开这个开关 mWebView.getSettings().setJavaScriptEnabled(true);
原生调用JS:
同方案一的原生调用JS-
JS调用原生:
我们可以暴露一个Java的Object给WebView供JS调用。什么意思?就是说JS可以调用我们Java对象的某些方法,这里说的某些方法就是@JavascriptInterface注解修饰的方法,示例如下:// JS代码 ========================= // 登录 app.login("13011112222", "123456"); // 登出 app.logout(); // 获取用户信息 var info = app.getLoginUser(); showResponse(info); // Android代码 ===================== private void setupWebView() { mWebView.addJavascriptInterface(new JsInterfaceLogic(this), "app"); } /** * 暴露出去给JS调用的Java对象 */ class JsInterfaceLogic { private BaseFragment mFragment; public JsInterfaceLogic(BaseFragment mFragment) { this.mFragment = mFragment; } @JavascriptInterface public void login(String account, String password) { mFragment.showNativeMessage(String.format("执行登录操作,账号为:%s,密码为:%s", account, password)); } @JavascriptInterface public void logout() { mFragment.showNativeMessage("执行【登出】操作"); } @JavascriptInterface public String getLoginUser() { return new JSONObject(new HashMap(4) {{ put("user_id", 666); put("username", "你就说6不6"); put("sex", "未知"); put("isStudent", false); }}).toString(); } }
方案三,JSBridge
说明:Android原生是不支持这种方式的,我们需要依赖于一个三方库 —— JsBridge,这是一个很有名的库,具体有多牛逼这里也不做过多需求,百度一下你就知道。
-
初始化代码:
// JS初始化代码 /** * 初始化jsbridge * @param readyCallback 初始化完成后的回调 */ function initJsBridge(readyCallback) { var u = navigator.userAgent; var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android终端 var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端 // 注册jsbridge function connectWebViewJavascriptBridge(callback) { if (isAndroid) { if (window.WebViewJavascriptBridge) { callback(WebViewJavascriptBridge) } else { document.addEventListener( 'WebViewJavascriptBridgeReady' , function () { callback(WebViewJavascriptBridge) }, false ); } return; } if (isiOS) { if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); } if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); } window.WVJBCallbacks = [callback]; var WVJBIframe = document.createElement('iframe'); WVJBIframe.style.display = 'none'; WVJBIframe.src = 'https://__bridge_loaded__'; document.documentElement.appendChild(WVJBIframe); setTimeout(function () { document.documentElement.removeChild(WVJBIframe) }, 0) } } // 调用注册方法 connectWebViewJavascriptBridge(function (bridge) { if (isAndroid) { bridge.init(function (message, responseCallback) { console.log('JS got a message', message); responseCallback(data); }); } // 只有在这里注册过的方法,在原声代码里才能用callHandler的方式调用 bridge.registerHandler('jsbridge_showMessage', function (data, responseCallback) { showResponse(data); }); bridge.registerHandler('jsbridge_getJsMessage', function (data, responseCallback) { showResponse(data); responseCallback('native 传过来的是:' + data); }); readyCallback(); }); } // Android初始化代码 mWeb.registerHandler("getOS", new BridgeHandler() { @Override public void handler(String data, CallBackFunction function) { HashMap response = new HashMap(){{ put("error", 0); put("message", ""); put("data", new HashMap(){{ put("os", "android"); }}); }}; function.onCallBack(response.toString()); } }); mWeb.registerHandler("login", new BridgeHandler() { @Override public void handler(String data, CallBackFunction function) { Gson gson = new Gson(); final User user = gson.fromJson(data, User.class); HashMap response = new HashMap(){{ put("error", 0); put("message", ""); put("data", String.format("执行登录操作,账号为:%s、密码为:%s", user.getAccount(), user.getPassword())); }}; function.onCallBack(response.toString()); } });
-
原生调JS
// 使用callHandler的方式调用JS中已经注册过的方法 mWeb.callHandler("jsbridge_getJsMessage", message, new CallBackFunction() { @Override public void onCallBack(String data) { showNativeMessage(String.format("原生调用JsBridge方法后,Js方法的返回值为:【%s】", data)); } });
-
JS调用原生
// 首先调用JSBridge初始化代码,完成后再设置其他 initJsBridge(function () { $("#getOS").click(function () { // 通过JsBridge调用原生方法,写法固定,第一个参数时方法名,第二个参数时传入参数,第三个参数时响应回调 window.WebViewJavascriptBridge.callHandler('getOS', null, function (response) { showResponse(response); }); }); });