目錄
示例代碼
Demo: https://github.com/gwpp/jsinterface
前言
在《App與Js交互(一)iOS》中我們?cè)敿?xì)的列舉了iOS與JS的各種交互方式嫩实,那么Android端的交互又是怎樣的呢?下面就來為大家一一介紹。
ps:本人iOS出身,Android學(xué)習(xí)時(shí)間不長(zhǎng),如果有BUG還請(qǐng)?jiān)谙路皆u(píng)論中及時(shí)反饋坛吁,感謝非常。
Android系統(tǒng)中的交互
方案一铐尚,攔截跳轉(zhuǎn)
-
初始化:
// WebView默認(rèn)是不支持Android&JS通信的拨脉,要在WebView初始化的時(shí)候打開這個(gè)開關(guān) mWebView.getSettings().setJavaScriptEnabled(true);
-
原生調(diào)用JS:
Android調(diào)用JS的常規(guī)方法有兩種,如下:-
方法 A:
mWebView.loadUrl("javascript:alert('1234')");
是的宣增,你沒有看錯(cuò)玫膀,就是渲染URL的方法,它也可以用來執(zhí)行js代碼爹脾,但是弊端就是執(zhí)行完了這段代碼后WebView上原有的內(nèi)容有可能會(huì)被覆蓋帖旨,并且拿不到JS方法的return值,所以一般不會(huì)使用這種方式灵妨。
-
方法 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); } });
這種方法會(huì)比方法A好很多解阅,首先不會(huì)影響WebView原本渲染的內(nèi)容,其次它還支持JS方法的返回值泌霍,所以在正常開發(fā)中更多時(shí)候用的是方法B货抄。
-
-
JS調(diào)用原生:
用過WebView的同學(xué)應(yīng)該都知道有個(gè)東西叫做WebViewClient
,這個(gè)東西就可以實(shí)現(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)被我“消費(fèi)”了积暖。 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)用原生方法時(shí)的特殊跳轉(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í)行登錄操作怪与,賬號(hào)為:%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初始化的時(shí)候打開這個(gè)開關(guān) mWebView.getSettings().setJavaScriptEnabled(true);
原生調(diào)用JS:
同方案一的原生調(diào)用JS-
JS調(diào)用原生:
我們可以暴露一個(gè)Java的Object給WebView供JS調(diào)用茎杂。什么意思错览?就是說JS可以調(diào)用我們Java對(duì)象的某些方法,這里說的某些方法就是@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對(duì)象 */ 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í)行登錄操作,賬號(hào)為:%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原生是不支持這種方式的,我們需要依賴于一個(gè)三方庫 —— JsBridge忌愚,這是一個(gè)很有名的庫曲管,具體有多牛逼這里也不做過多需求,百度一下你就知道硕糊。
-
初始化代碼:
// 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終端 // 注冊(cè)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)用注冊(cè)方法 connectWebViewJavascriptBridge(function (bridge) { if (isAndroid) { bridge.init(function (message, responseCallback) { console.log('JS got a message', message); responseCallback(data); }); } // 只有在這里注冊(cè)過的方法院水,在原聲代碼里才能用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í)行登錄操作,賬號(hào)為:%s简十、密碼為:%s", user.getAccount(), user.getPassword())); }}; function.onCallBack(response.toString()); } });
-
原生調(diào)JS
// 使用callHandler的方式調(diào)用JS中已經(jīng)注冊(cè)過的方法 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)用原生方法螟蝙,寫法固定恢恼,第一個(gè)參數(shù)時(shí)方法名,第二個(gè)參數(shù)時(shí)傳入?yún)?shù)胰默,第三個(gè)參數(shù)時(shí)響應(yīng)回調(diào) window.WebViewJavascriptBridge.callHandler('getOS', null, function (response) { showResponse(response); }); }); });