App与Js交互(二)Android

目录

  • 《App与Js交互(一)iOS》
  • 《App与Js交互(二)Android》
  • 《App与Js交互(三)Android、iOS通用解决方案推荐》

示例代码

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() {
        @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);
          });
      });
     });
    

你可能感兴趣的:(App与Js交互(二)Android)