JsBridge中的H5与Native通信原理解析

目前大多项目都是采用 Native+H5 的方式进行开发与迭代,随着需求的不断增多,
两者之间的通信,绝对是不可避免,Android系统提供的java与js通信方式是有一些安全问题的。
那怎么办呢?目前据我了解,两者通信首选方案应该是JsBridge
由阿里巴巴的开发hi大头鬼hi和其他开源自愿者,一同维护提供的一套开源框架。
今天我们来了解使用它吧!


JsBridge简介

JsBridge是Java和JavaScript之间的桥梁,主要提供的是Java和JavaScript之间的数据交换的功能。

JsBridge如何使用

JsBridge库集成

在相关build.gradle加入集成代码

1
2
3
4
5
6
7
repositories {  
maven {url "https://jitpack.io"}
}

dependencies {
compile 'com.github.lzyzsd:jsbridge:1.0.4'
}

JsBridge库使用

  • xml布局中,使用BridgeWebView

    1
    2
    3
    4
    <com.github.lzyzsd.jsbridge.BridgeWebView
    android:id="@+id/webView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

    Native提供方法、Js调用

    • Native提供方法
      • submitFromWeb 是提供的方法名,可自定义修改
      • data 表示的是h5传递过来的参数
      • function 是h5提供的回调方法,将原生数据再回传给h5
    1
    2
    3
    4
    5
    6
    webView.registerHandler("submitFromWeb", new BridgeHandler(){
    @Override
    public void handler(String data, CallBackFunction function){
    function.onCallBack("submitFrom web exe, response data from java");
    }
    }
    • Js调用原生方法
      • submitFromWeb 对应上面提供的方法名
      • {‘param’:str1} 对应上面的data
      • function(responseData){} 对应上面的回调
    1
    2
    3
    4
    5
    6
    7
    8
    window.WebViewJavascriptBridge.callHandler(
    'submitFromWeb',
    {'param':str1},
    function(responseData){
    //这里打印的应该是上面Handler实现方法中的callback的入参:submitFrom web exe, response data from java
    document.getElementById("show").innerHTML = "response data from java, data = "+responseData
    }
    )
    • JsBridge 也提供了无方法名的调用方式 如:DefaultHandler
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      package com.github.lzyzsd.jsbridge;

      public class DefaultHandler implements BridgeHandler{

      String TAG = "DefaultHandler";

      @Override
      public void handler(String data, CallBackFunction function) {
      if(function != null){
      function.onCallBack("DefaultHandler response data");
      }
      }

      }
      具体使用方式如下:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      //java提供
      webView.setDefaultHandler(new DefaultHandler());
      //js 调用
      window.WebViewJavascriptBridge.send(
      data,
      function(responseData){
      //java中DefaultHandler所实现的方法中callback所定义的入参
      }
      )

Js提供方法、Native调用

基本和上述提供调用差不多,代码如下

  • Js提供方法
    1
    2
    3
    4
    5
    window.WebViewJavascriptBridge.registerHandler("functionInJs", function(data, responseCallback) {
    document.getElementById("show").innerHTML = ("data from Java: = " + data);
    var responseData = "Javascript Says Right back aka!";
    responseCallback(responseData);
    });
  • Java调用Js方法

    1
    2
    3
    4
    5
    6
    7
    webView.callHandler("functionInJs", new Gson().toJson(user),
    new CallBackFunction(){
    @Override
    public void onCallBack(String data){
    }
    }
    );
  • 类似java,js设置默认Handler

    1
    2
    3
    4
    5
    6
    7
    8
    bridge.init(function(message, responseCallback) {
    console.log('JS got a message', message);
    var data = {
    'Javascript Responds': 'Wee!'
    };
    console.log('JS responding with', data);
    responseCallback(data);
    });

    java调用方式如下:

    1
    webView.send("hello");

JsBridge源码介绍

  • js 相关

    • WebViewJavascriptBridge.js
      注入到各个html的js文件,提供初始化,注册、调用Handler等方法。
  • java相关
类名 作用
BridgeWebView.java WebView的子类,提供注册、调用Handler等方法
BridgeUtil.java 工具类,提供从url中提取数据,获取回调方法,注入js等方法
WebViewJavascriptBridge.java bridge接口文件,定义了发送数据的方法,由BridgeWebView来实现
BridgeWebViewClient.java WebViewClient子类,重写了ShouldOverrideUrlLoading、onPageFinish、onPageStart等方法
BridgeHandler.java Java与Js交互的载体,Java&Js通过Handler的名称来找到响应的Handler来操作
DefaultBridgeHandler.java BridgeHandler的子类,不做任何操作。仅为Java提供默认的接收数据的Handler
CallBackFunction.java 回调函数,Handler处理完成后,用来给Js发送数据
Message.java 消息对象,用来封装与js交互时的json数据,callid、responseid等

JsBridge原理解析

JsBridge调用过程

  • Native初始化Webview,注册Handler;加载页面完成后,将WebViewJavascriptBridge.js文件注入html(webview.loadUrl(“javascript:WebViewJavascriptBridge.js的内容”);)。查询消息队列是否有信息需要被接收。

  • H5页面初始化,注册Handler,查询消息队列是否有信息需要被接收。

  • 用户操作,H5调用本地功能:Js将消息内容放在sendMessageQueue中,并设置iframe的src为yy://QUEUE_MESSAGE/

  • Webview设置的WebViewClient拦截到约定url,调用Webview的刷新消息队列的方法flushMessageQueue,此方法就是加载了一个url:javascript:WebViewJavascriptBridge._fetchQueue();这也是Js中定义的方法,另外定义了一个回调;回调方法主要做了两件事:①判断Native是否为此返回数据保有响应回调操作,若有,则执行,若没有,则判断callId,不为空时为这个callId初始化一个回调。②通过handlername判断是否为默认的Handler还是自定义的Handler,调用相应Handler的handler方法,入参为消息数据内容和第一步中定义的回调。

  • Js中_fetchQueue设置了iframe的src,内容为:yy://return/_fetchQueue/+第二步中放入sendMessageQueue中的消息内容。

  • WebViewClient拦截到url为yy://return/,调用WebView的handlerReturnData方法;通过url中定义的方法名,找到第四个步骤中定义的回调,并调用。回调方法走完后,删除此回调方法。

  • 如果Js在调用Handler的时候设置了回调方法,也就是在第四步骤中的含有callId,就会调用queueMessage的方法,然后往下就是走Native给Js发送消息的步骤。

  • Ps: Native给Js发送消息的步骤跟上述从第三步骤到第七步骤完全相同,只不过Native和Js对象调换位置即可

JsBridge的核心

  • 重写WebViewClient的shouldOverrideUrlLoading(WebView view, String url)方法拦截url
    • 向body中添加一个不可见的iframe元素。通过拦截url的方法来执行相应的操作。页面本身不能跳转,所以改变一个不可见的iframe的src就可以让webview拦截到url,而用户是无感知的。
  • 通过拦截的url特殊标记,调用对应的注册方法
  • 再通过 loadUrl(“javascript:js_method()”)或loadUrl(String jsUrl, CallBackFunction returnCallback);方式将数据再传递给js

相关推荐