目錄
示例代碼
Demo: https://github.com/gwpp/jsinterface
前言
在《App與Js交互(一)iOS》中我們詳細(xì)的列舉了iOS與JS的各種交互方式,那么Android端的交互又是怎樣的呢?下面就來為大家一一介紹。
ps:本人iOS出身,Android學(xué)習(xí)時間不長,如果有BUG還請在下方評論中及時反饋,感謝非常。
Android系統(tǒng)中的交互
方案一,攔截跳轉(zhuǎn)
-
初始化:
// WebView默認(rèn)是不支持Android&JS通信的,要在WebView初始化的時候打開這個開關(guān) mWebView.getSettings().setJavaScriptEnabled(true); -
原生調(diào)用JS:
Android調(diào)用JS的常規(guī)方法有兩種,如下:-
方法 A:
mWebView.loadUrl("javascript:alert('1234')");是的,你沒有看錯,就是渲染URL的方法,它也可以用來執(zhí)行js代碼,但是弊端就是執(zhí)行完了這段代碼后WebView上原有的內(nèi)容有可能會被覆蓋,并且拿不到JS方法的return值,所以一般不會使用這種方式。
-
方法 B:
String js = "getCookieWithKey('username')"; mWebView.evaluateJavascript(js, new ValueCallback<String>() { @Override public void onReceiveValue(String s) { // 這里可以處理被調(diào)用js方法的return showNativeMessage("調(diào)用JS方法后得到的返回值是:" + s); } });這種方法會比方法A好很多,首先不會影響WebView原本渲染的內(nèi)容,其次它還支持JS方法的返回值,所以在正常開發(fā)中更多時候用的是方法B。
-
-
JS調(diào)用原生:
用過WebView的同學(xué)應(yīng)該都知道有個東西叫做WebViewClient,這個東西就可以實現(xiàn)我們的需求——攔截跳轉(zhuǎn)。// JS代碼 =========================== // 登錄 window.location = 'app://login?account=13011112222&password=123456'; // 登出 window.location = 'app://share?title=分享的標(biāo)題&desc=分享的描述' // Android代碼 ======================= private void setupWebView() { WebViewClient webViewClient = new WebViewClient() { // 老方法 @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { Uri uri = Uri.parse(url); // 如果攔截單的鏈接是app協(xié)議的就說明是我們需要處理的鏈接 if (uri.getScheme().contentEquals("app")) { callNative(uri); // 返回true就是告訴WebView該鏈接不需要你處理了,已經(jīng)被我“消費”了。 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 調(diào)用原生方法時的特殊跳轉(zhuǎn)鏈接 * @param uri 特殊的跳轉(zhuǎn)鏈接 */ 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("執(zhí)行登錄操作,賬號為:%s,密碼為:%s", account, password)); } else if (host.contentEquals("share")) { String title = uri.getQueryParameter("title"); String desc = uri.getQueryParameter("desc"); showNativeMessage(String.format("執(zhí)行分享操作,title為:【%s】,desc為:【%s】", title, desc)); } }
方案二,JavaScriptInterface
-
初始化:
// WebView默認(rèn)是不支持Android&JS通信的,要在WebView初始化的時候打開這個開關(guān) mWebView.getSettings().setJavaScriptEnabled(true); 原生調(diào)用JS:
同方案一的原生調(diào)用JS-
JS調(diào)用原生:
我們可以暴露一個Java的Object給WebView供JS調(diào)用。什么意思?就是說JS可以調(diào)用我們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調(diào)用的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("執(zhí)行登錄操作,賬號為:%s,密碼為:%s", account, password)); } @JavascriptInterface public void logout() { mFragment.showNativeMessage("執(zhí)行【登出】操作"); } @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 初始化完成后的回調(diào) */ 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) } } // 調(diào)用注冊方法 connectWebViewJavascriptBridge(function (bridge) { if (isAndroid) { bridge.init(function (message, responseCallback) { console.log('JS got a message', message); responseCallback(data); }); } // 只有在這里注冊過的方法,在原聲代碼里才能用callHandler的方式調(diào)用 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("執(zhí)行登錄操作,賬號為:%s、密碼為:%s", user.getAccount(), user.getPassword())); }}; function.onCallBack(response.toString()); } }); -
原生調(diào)JS
// 使用callHandler的方式調(diào)用JS中已經(jīng)注冊過的方法 mWeb.callHandler("jsbridge_getJsMessage", message, new CallBackFunction() { @Override public void onCallBack(String data) { showNativeMessage(String.format("原生調(diào)用JsBridge方法后,Js方法的返回值為:【%s】", data)); } }); -
JS調(diào)用原生
// 首先調(diào)用JSBridge初始化代碼,完成后再設(shè)置其他 initJsBridge(function () { $("#getOS").click(function () { // 通過JsBridge調(diào)用原生方法,寫法固定,第一個參數(shù)時方法名,第二個參數(shù)時傳入?yún)?shù),第三個參數(shù)時響應(yīng)回調(diào) window.WebViewJavascriptBridge.callHandler('getOS', null, function (response) { showResponse(response); }); }); });