App與Js交互(二)Android

目錄

示例代碼

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);
          });
      });
     });
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容