JsBridge

51 篇文章 0 订阅

1、android与js的交互之jsbridge使用

2、JsBridge实现及原理

3、jsbridge流程图(大)

4、JsBridge 源码分析

5、WebView JS交互 JSBridge 案例 原理 MD

6、Hybrid APP基础篇(四)->JSBridge的原理

7、github传送门:https://github.com/lzyzsd/JsBridge

JS调用Android有三种方式:

  • webView.addJavascriptInterface()
  • WebViewClient.shouldOverrideUrlLoading()
  • WebChromeClient.onJsAlert()/onJsConfirm()/onJsPrompt() 方法分别回调拦截JS对话框alert()、confirm()、prompt()消息
  • WebChromeClient.onConsoleMessage()

Android调用JS有两种方式:

  • webView.loadUrl();
  • webView.evaluateJavascript()

gradle配置

在project的build.gradle中引入jitpack.io

allprojects {
    repositories {
        google()
        jcenter()
        maven { url "https://jitpack.io" }
    }
}

在module中添加依赖:

implementation 'com.github.lzyzsd:jsbridge:1.0.4'

Java端:注册提供给JS端调用的接口

Register a Java handler function so that js can call:

webView.registerHandler("submitFromWeb", new BridgeHandler() {
    @Override
    public void handler(String data, CallBackFunction function) {
        Log.i(TAG, "handler = submitFromWeb, data from web = " + data);
        //do something
        function.onCallBack("submitFromWeb exe, response data from Java"); //回调
    }
});

js can call this Java handler method "submitFromWeb" through:

WebViewJavascriptBridge.callHandler(
    'submitFromWeb'  //方法名
    , {'param': str1}  //参数
    , function(responseData) { //回调
        document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData
    }
);

You can set a default handler in Java, so that js can send message to Java without assigned handlerName:

webView.setDefaultHandler(new DefaultHandler());
window.WebViewJavascriptBridge.send(  //没有方法名
    data
    , function(responseData) {
        document.getElementById("show").innerHTML = "repsonseData from java, data = " + responseData
    }
);

JS端:注册提供给Java端调用的接口

Register a JavaScript handler function so that Java can call:

WebViewJavascriptBridge.registerHandler("functionInJs", function(data, responseCallback) {
    document.getElementById("show").innerHTML = ("data from Java: = " + data);
    var responseData = "Javascript Says Right back aka!";
    responseCallback(responseData);
});

Java can call this js handler function "functionInJs" through:

webView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() {
    @Override
    public void onCallBack(String data) {
    }
});

You can also define a default handler use init method, so that Java can send message to js without assigned handlerName:
for example:

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);
});

when Java call:

webView.send("hello");

will print 'JS got a message hello' and 'JS responding with' in webview console.

JS 端注意事项

This lib will inject注入 a WebViewJavascriptBridge Object to window object. So in your js, before use WebViewJavascriptBridge, you must detect检测 if WebViewJavascriptBridge exist. If WebViewJavascriptBridge does not exit, you can listen to WebViewJavascriptBridgeReady event, as the blow code shows:

if (window.WebViewJavascriptBridge) {
    //do your work here
} else {
    document.addEventListener(
        'WebViewJavascriptBridgeReady'
        , function() {
            //do your work here
        },
        false
    );
}

Js调用Java,Java调用Js

在Android开发中,能实现Js调用Java,有4种方法:

  1. JavascriptInterface
  2. WebViewClient.shouldOverrideUrlLoading()
  3. WebChromeClient.onConsoleMessage()
  4. WebChromeClient.onJsPrompt()

JavascriptInterface

这是Android提供的Js与Native通信的官方解决方案。
首先Java代码要实现这么一个类,它的作用是提供给Js调用。

    public class JavascriptInterface {
     
        @JavascriptInterface
        public void showToast(String toast) {
            Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show();
        }
    }

然后把这个类添加到WebView的JavascriptInterface中。webView.addJavascriptInterface(new JavascriptInterface(), “javascriptInterface”); 在Js代码中就能直接通过“javascriptInterface”直接调用了该Native的类的方法。

    function showToast(toast) {
        javascript:javascriptInterface.showToast(toast);
    }

但是这个官方提供的解决方案在Android4.2之前存在严重的安全漏洞。在Android4.2之后,加入了@JavascriptInterface才得到解决。所以考虑到兼容低版本的系统,JavascriptInterface并不适合。


WebViewClient.shouldOverrideUrlLoading()

这个方法的作用是拦截所有WebView的Url跳转。页面可以构造一个特殊格式的Url跳转,shouldOverrideUrlLoading拦截Url后判断其格式,然后Native就能执行自身的逻辑了。

    public class CustomWebViewClient extends WebViewClient {
     
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            if (isJsBridgeUrl(url)) {
                // JSbridge的处理逻辑
                return true;
            }
            return super.shouldOverrideUrlLoading(view, url);
        }
    }

WebChromeClient.onConsoleMessage()

这是Android提供给Js调试在Native代码里面打印日志信息的API,同时这也成了其中一种Js与Native代码通信的方法。在Js代码中调用console.log(‘xxx’)方法。

console.log('log message that is going to native code')


就会在Native代码的WebChromeClient.consoleMessage()中得到回调。consoleMessage.message()获得的正是Js代码console.log(‘xxx’)的内容。

    public class CustomWebChromeClient extends WebChromeClient {
     
        @Override
        public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
            super.onConsoleMessage(consoleMessage);
            String msg = consoleMessage.message();//JavaScript输入的Log内容
        }
    }

WebChromeClient.onJsPrompt()

其实除了WebChromeClient.onJsPrompt(),还有WebChromeClient.onJsAlert()和WebChromeClient.onJsConfirm()。顾名思义,这三个Js给Native代码的回调接口的作用分别是展示提示信息,展示警告信息和展示确认信息。鉴于,alert和confirm在Js的使用率很高,所以JSBridge的解决方案中都倾向于选用onJsPrompt()。
Js中调用

window.prompt(message, value)

WebChromeClient.onJsPrompt()就会受到回调。onJsPrompt()方法的message参数的值正是Js的方法window.prompt()的message的值。

    public class CustomWebChromeClient extends WebChromeClient {
     
        @Override
        public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
            // 处理JS 的调用逻辑
            result.confirm();
            return true;
        }
    }

Java调用Js

前文提到的4种通信方式都是Js通信Native的Java,而反过来,Java通信Js只有一种方式。那就是调用WebView.loadUrl()去执行一个预先定义好的Js方法。

webView.loadUrl(String.format("javascript:WebViewJavascriptBridge._handleMessageFromNative(%s)", data));

 

源码解析

主要有三点:

  • Android调用JS是通过loadUrl(url),url中可以拼接要传给JS的对象
  • JS调用Android是通过shouldOverrideUrlLoading
  • JsBridge将沟通数据封装成Message,然后放进Queue,再将Queue进行传输

BridgeWebView

首先自定义了一个 WebView:

public class BridgeWebView extends WebView implements WebViewJavascriptBridge

继承的是系统自带的 WebView,鉴于很多人会使用腾讯的 X5 WebView,如果想保持使用 X5 的话,我们其实重新定义一个 WebView 并将其继承的 WebView 改成 X5 的就行了。

里面定义了两个集合,一个用于在调用 registerHandler 时保存Java中注册的 方法名 和 BridgeHandler 的映射,一个用于在调用 send 时保存 Java 向 JS 发送的请求和 CallBackFunction 的映射:

Map<String, BridgeHandler> messageHandlers = new HashMap<>();//供js调用
Map<String, CallBackFunction> responseCallbacks = new HashMap<>();//调用js

然后是一些初始化操作:

getSettings().setJavaScriptEnabled(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
   WebView.setWebContentsDebuggingEnabled(true);
}
setWebViewClient(new BridgeWebViewClient(this));

这里定义了一个 BridgeWebViewClient,里面并没有什么复杂的逻辑,仅仅是重写了 WebViewClient 的 shouldOverrideUrlLoading 方法和 onPageFinished 方法。我们先看一下 onPageFinished 方法,shouldOverrideUrlLoading 后面我们用到时再分析。

onPageFinished 主要用于在页面加载后加载 js 文件,以及调用一些需要在页面加载后才能调用的 js 逻辑:

public static final String toLoadJs = "WebViewJavascriptBridge.js";

@Override
public void onPageFinished(WebView view, String url) {
    super.onPageFinished(view, url);
    String jsContent = assetFile2Str(view.getContext(), toLoadJs);
    view.loadUrl("javascript:" + jsContent); //加载 JS 代码
    if (webView.getStartupMessage() != null) {
        for (Message m : webView.getStartupMessage()) {
            webView.dispatchMessage(m);//需要在页面加载完执行的逻辑
        }
        webView.setStartupMessage(null);//清空这些任务
    }
}

java 调用 js 中注册的方法

调用过程

接口 WebViewJavascriptBridge 其实就是让我们调用 js 中注册的方法的:

public interface WebViewJavascriptBridge {
    void send(String data);
    void send(String data, CallBackFunction responseCallback);
}

鉴于 js 和 java 交互无非这两种方式:webview.addJavascriptInterfaceWebViewClient.shouldOverrideUrlLoading,所以其实不用看源码我们也知道,其最终肯定是通过两者中的一种来实现的。

我们大致瞅一下其调用过程:

send(String data) -> send(data, null);
send(String data, CallBackFunction responseCallback) -> doSend(null, data, responseCallback);

两者都会调用 doSend 方法,这个方法其实主要就是将消息相关信息封装到 Message 中:

private void doSend(String handlerName, String data, CallBackFunction responseCallback) {
      Message m = new Message();
      if (!TextUtils.isEmpty(handlerName)) m.setHandlerName(handlerName);
      if (!TextUtils.isEmpty(data)) m.setData(data);
      if (responseCallback != null) {
            //为此次调用定义一个唯一的回调id,例如【JAVA_CB_1_541】【JAVA_CB_2_455】
            String callbackStr = String.format(BridgeUtil.CALLBACK_ID_FORMAT, ++uniqueId + (BridgeUtil.UNDERLINE_STR + SystemClock.currentThreadTimeMillis()));
            responseCallbacks.put(callbackStr, responseCallback);//将回调id和对应的回调方法保存起来
            m.setCallbackId(callbackStr);//将回调id赋给此消息,此id是实现交互的最关键的信息
            //之后,当具有同一id的消息通过 shouldOverrideUrlLoading 回调给我们时,我们就可以找到并执行这里定义的回调方法
      }
      queueMessage(m);
}

这里用到的 Message 是库中定义的一个非常简单的数据结构:

public class Message{
    private String callbackId; //callbackId
    private String responseId; //responseId
    private String responseData; //responseData
    private String data; //data of message
    private String handlerName; //name of handler

    private final static String CALLBACK_ID_STR = "callbackId";
    private final static String RESPONSE_ID_STR = "responseId";
    private final static String RESPONSE_DATA_STR = "responseData";
    private final static String DATA_STR = "data";
    private final static String HANDLER_NAME_STR = "handlerName";
}

接下来会做一个简单的判断,以决定是立即调用还是在页面加载完成后调用:

private void queueMessage(Message m) {
      if (startupMessage != null) startupMessage.add(m); //在 onPageFinished 后调用
      else dispatchMessage(m); //立即调用(默认操作)
}

下面是最核心的部分之一了,简单来说就是,经过一系列操作后,最终是通过 loadUrl 来实现原生调用 js 的方法的:

void dispatchMessage(Message m) {
      String messageJson = m.toJson(); //以Json格式通讯
      messageJson = messageJson.replaceAll("(\\\\)([^utrn])", "\\\\\\\\$1$2");//转义特殊字符
      messageJson = messageJson.replaceAll("(?<=[^\\\\])(\")", "\\\\\"");//转义特殊字符
      String javascriptCommand = String.format(BridgeUtil.JS_HANDLE_MESSAGE_FROM_JAVA, messageJson);//调用的js命令

      if (Thread.currentThread() == Looper.getMainLooper().getThread()) { //必须在主线程中调用
            this.loadUrl(javascriptCommand); //最核心的部分,原理就是通过 loadUrl 调用 js 方法
      }
}

js命令的全部内容例如下,其中包含的内容有:唯一的callbackId、js中定义的方法名handlerName、传递过去的数据data

javascript:WebViewJavascriptBridge._handleMessageFromNative('{\"callbackId\":\"JAVA_CB_1_541\",\"data\":\"【包青天20094】\",\"handlerName\":\"showInHtml\"}');

至此,整个调用过程结束了。

回调过程

经过上面的操作,我们已经调用了 js 端的代码了,下面我们探究在调用时我们注册的回调方法是怎么被调用的。

我们不需要关心 js 那端的逻辑是怎么样的,因为这完全是前端同学需要关注的事情,我们只关注 js 调用之后是怎么和我们原生交互的。

前面我们已经说过了,js和java交互只有两种方式:webview.addJavascriptInterfaceWebViewClient.shouldOverrideUrlLoading,这个库采用的就是重写 shouldOverrideUrlLoading 这种方式。

我们首先看一下 shouldOverrideUrlLoading 这个方法,里面主要是根据回调 url 做不同的处理:

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (url.startsWith(BridgeUtil.YY_RETURN_DATA)) {//格式为【yy://return/{function}/returncontent】
        webView.handlerReturnData(url);
        return true;
    } else if (url.startsWith(BridgeUtil.YY_OVERRIDE_SCHEMA)) { //格式为【yy://】
        webView.flushMessageQueue();
        return true;
    } else return super.shouldOverrideUrlLoading(view, url); //不做任何处理
}

上面的案例中,我们回调的 url 为yy://__QUEUE_MESSAGE__/,所以会调用flushMessageQueue方法,从方法名来看,其作用应该是"刷新消息队列"的,我们先看看其基本结构:

void flushMessageQueue() {
   if (Thread.currentThread() == Looper.getMainLooper().getThread()) { //主线程的调用才处理
       //JS代码为【"javascript:WebViewJavascriptBridge._fetchQueue();"】
      loadUrl(BridgeUtil.JS_FETCH_QUEUE_FROM_JAVA, new CallBackFunction() {
         @Override
         public void onCallBack(String data) { //处理调用后的回调
          //...
      });
   }
}
public void loadUrl(String jsUrl, CallBackFunction returnCallback) {
   this.loadUrl(jsUrl);//调用 js 代码
   responseCallbacks.put(BridgeUtil.parseFunctionName(jsUrl), returnCallback); //处理后的 id 为【_fetchQueue】
}

调用完 loadUrl 之后集合 responseCallbacks 中就有两个元素了。既然又调用了一次 loadUrl,那我们就继续追踪shouldOverrideUrlLoading。这一次回调的 url 为:

yy://return/_fetchQueue/[{"responseId":"JAVA_CB_1_541","responseData":"【你好,我是白乾涛,这是JS名为showInHtml的Handler返回的数据】"}]

所以就会调用handlerReturnData这个方法,这个方法是用于将返回的数据回调给 CallBackFunction 的,逻辑非常简单:

void handlerReturnData(String url) {
   String functionName = BridgeUtil.getFunctionFromReturnUrl(url); //固定为【_fetchQueue】
   CallBackFunction f = responseCallbacks.get(functionName); //找到对应的 CallBackFunction
   String data = BridgeUtil.getDataFromReturnUrl(url); //拿到 js 返回的数据
   if (f != null) {
      f.onCallBack(data); //将返回的数据回调给 CallBackFunction
      responseCallbacks.remove(functionName);//移除此 CallBackFunction
      return;
   }
}

解析后,这里的:

  • functionName 为 _fetchQueue
  • CallBackFunction 为上面我们在调用 flushMessageQueue 时定义的回调方法
  • 返回的 data 为 [{"responseId":"JAVA_CB_1_541","responseData":"【你好,我是白乾涛,这是JS名为showInHtml的Handler返回的数据】"}],可以看到这个 data 其实是一个 JSONArray

所以接下来就进入其 onCallBack 中了,这个方法里面的代码稍多一点点,大致逻辑为:

List<Message> list = Message.toArrayList(data); //解析返回的数据
for (int i = 0; i < list.size(); i++) { //遍历这个集合,本例中只有id为 JAVA_CB_1_541 的元素
    if (!TextUtils.isEmpty(responseId) { //如果消息的responseId不为空
        function.onCallBack(responseData); //【核心】,调用相应的回调方法
        responseCallbacks.remove(responseId); //执行完就移除掉
    }else{ //这里面的逻辑是在js主动调用原生方法时才会执行的,我们后面再分析
    }
}

完成以后,所有的回调都执行了,集合 responseCallbacks 中的所有元素也都移除了。至此整个回调过程也结束了。

一个疑惑

通过上面的流程分析,我们知道,在我们首次 loadUrl 后的 shouldOverrideUrlLoading 回调中,我们首先是进入 flushMessageQueue 方法里面又 load 了一段固定的 js 代码,然后再在 shouldOverrideUrlLoading 回调中获取 js 返回的数据以及执行回调,为什么要这么搞呢,明明一次交互就可以了的?

js 调用 java 中注册的方法

 

这里的大多数流程和上面的基本是一致的,下面我们只简单的过一下:

  • 首先肯定还是回调 shouldOverrideUrlLoading ,此时回调的url为固定的yy://__QUEUE_MESSAGE__/
  • 接着调用 flushMessageQueue,然后又调用一次 loadUrl,消息 id 同样为_fetchQueue
  • 然后会再次回调 shouldOverrideUrlLoading ,此时的url为:
yy://return/_fetchQueue/[{"handlerName":"showTips","data":{"type":"dialog","title":"我是标题","msg":""},"callbackId":"cb_3_1540110644942"}]
  • 所以会进入 handlerReturnData,接着会回调flushMessageQueue中的 onCallBack方法,接着就是遍历集合
  • 目前集合中只有一个元素,由于 responseId 为空,所以会走和上面分析的 java 调 js 不同的分支我们着重分析此时要走的逻辑:
CallBackFunction responseFunction = null; //临时定义一个回调
final String callbackId = m.getCallbackId();
if (!TextUtils.isEmpty(callbackId)) { //有 callbackId 时的回调是继续load一个js代码
   responseFunction = new CallBackFunction() {
      @Override
      public void onCallBack(String data) {
         Message responseMsg = new Message();
         responseMsg.setResponseId(callbackId); //将 callbackId作为 responseId
         responseMsg.setResponseData(data);
         queueMessage(responseMsg);
      }
   };
} else { //没有 callbackId 时的回调是一个空实现(正常是不会走到这里的)
   responseFunction = data -> { };
}
BridgeHandler handler = TextUtils.isEmpty(m.getHandlerName() ? defaultHandler : messageHandlers.get(m.getHandlerName());
if (handler != null) handler.handler(m.getData(), responseFunction); //调用我们定义的 handler

里面首先是定义了一个 CallBackFunction 回调,然后会调用我们定义的 BridgeHandlerhandler 方法,例如下面是本案例中我写的部分逻辑:

@Override
public void handler(String data, CallBackFunction function) {
    //...显示Toast或弹Dialog或打印Log
    function.onCallBack(...); //返回数据
}

接下来就会走我们上面定义的 responseFunction 的 onCallBack 方法,接着会走 queueMessage 方法,和上面的流程一样,我们接着会走 dispatchMessage,然后调用 loadUrl 调用 js 的方法的,此时的 js 命令为:

javascript:WebViewJavascriptBridge._handleMessageFromNative('{\"responseData\":\"{\\\"status\\\":\\\"success\\\",\\\"time\\\":0}\",\"responseId\":\"cb_2_1540112210451\"}');

至此,便完整实现了 js 调用原生方法,并在原生响应 js 的命令后回调数据给 js。

一个完整的Demo

MainActivity

public class MainActivity extends ListActivity {
    private BridgeWebView mBridgeWebView;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBridgeWebView = new BridgeWebView(this);
        registerHandler();
        mBridgeWebView.loadUrl("file:///android_asset/jsbridge.html");
        getListView().addFooterView(mBridgeWebView);

        String[] array = {"调用JS中的名为showInHtml的Handler",
                "调用JS中的默认Handler,没有回调",
                "调用JS中的默认Handler,有回调",};
        setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, new ArrayList<>(Arrays.asList(array))));
    }

    private void registerHandler() {
        mBridgeWebView.setDefaultHandler(new DefaultBridgeHandler());//默认的Handle
        mBridgeWebView.registerHandler(JBNativeIDs.BRIDGE_ID_DEVICEINFO, new DeviceInfoBridgeHandler());//获取设备信息
        mBridgeWebView.registerHandler(JBNativeIDs.BRIDGE_ID_BACKNATIVE, new BackNativeBridgeHandler(this));//结束当前Activity
        mBridgeWebView.registerHandler(JBNativeIDs.BRIDGE_ID_SHOWTIPS, new ShowTipsBridgeHandler(this));//显示原生toast或弹窗
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        switch (position) {
            case 0:
                mBridgeWebView.callHandler(JBJsIDs.BRIDGE_ID_SHOWINHTML, "【包青天20094】", new SimpleCallBackFunction());
                break;
            case 1:
                mBridgeWebView.send("【包青天20095】");
                break;
            case 2:
                mBridgeWebView.send("【包青天20096】", new SimpleCallBackFunction());
                break;
        }
    }

    class SimpleCallBackFunction implements CallBackFunction {
        @Override
        public void onCallBack(String data) {
            Log.i("bqt", "【JS回调】,是否在主线程:" + (Looper.myLooper() == Looper.getMainLooper()) + "\n" + data);
            //也可以用Thread.currentThread() == Looper.getMainLooper().getThread()
        }
    }
}

注册的BridgeHandler

public class DeviceInfoBridgeHandler implements BridgeHandler {
    /**
     * @param data js调用原生所传递的参数
     * @param function js调用原生所传递的回调接口
     */
    @Override
    public void handler(String data, final CallBackFunction function) {
        Log.i("bqt", "【获取设备信息,参数】" + data);

        new Thread(() -> {
            SystemClock.sleep(2000);//模拟耗时操作
            DeviceInfo deviceInfo = new DeviceInfo("小米6", "8.0");
            JBRespBean bridgeRespBean = JBRespBean.newBuilder()
                    .status(JBRespBean.STATUS_SUCCESS)
                    .time(System.currentTimeMillis())
                    .data(deviceInfo)
                    .build();

            final String response = new Gson().toJson(bridgeRespBean);
            Log.i("bqt", "【给JS的响应】" + response);

            new Handler(Looper.getMainLooper()).post(() -> {
                function.onCallBack(response);//必须在主线程中回调给JS,因为JS中加载网页也是UI操作,也必须在主线程中
            });
        }).start();
    }

    static class DeviceInfo {
        String phoneName;
        String sysVersion;

        DeviceInfo(String phoneName, String sysVersion) {
            this.phoneName = phoneName;
            this.sysVersion = sysVersion;
        }
    }
}

HTML+JS源码

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>JSBridge测试页面</title>
</head>
<body>
<p>
    <!--xmp标签可以在页面上输出html代码-->
    <xmp id="show"></xmp>
</p>
<p><input type="text" id="inputMsg" value=""/></p>
<p><input type="button" id="button1" value="获取设备信息" onclick="deviceInfo();"/></p>
<p><input type="button" id="button2" value="显示原生dialog" onclick="showTips();"/></p>
<p><input type="button" id="button3" value="结束当前Activity" onclick="backNative();"/></p>
<p><input type="button" id="button4" value="使用默认的Handle" onclick="defaultHandler();"/></p>
<p><input type="button" id="button5" value="显示html源码" onclick="showHtml();"/></p>
<p>
    <xmp id="htmlContent"></xmp>
</p>
</body>
<script>
        //******************************************************************************************
        // 点击事件
        //******************************************************************************************
        function deviceInfo() {
            var inputMsg = document.getElementById("inputMsg").value;
            var data = {
                content: inputMsg
            };
            window.WebViewJavascriptBridge.callHandler("deviceInfo",
                data,
                function(responseData) {
                    document.getElementById("show").innerHTML = "【返回数据】" + responseData
                }
            );
        }
        function showTips() {
            var inputMsg = document.getElementById("inputMsg").value;
            var data = {
                type: "dialog",
                title: "我是标题",
                msg: inputMsg
            };
            window.WebViewJavascriptBridge.callHandler('showTips',
                data,
                function(responseData) {
                    document.getElementById("show").innerHTML = "【返回数据】 " + responseData
                }
            );
        }
        function backNative() {
            window.WebViewJavascriptBridge.callHandler('backNative',
                null,
                function(responseData) {
                    console.log("【此时Activity关闭了,不要再操作UI了】" + responseData);
                }
            );
        }
        function defaultHandler() {
            var data = {
                type: "toast",
                msg: "使用默认的Handle处理"
            };
            window.WebViewJavascriptBridge.send(data,
                function(responseData) {
                    window.WebViewJavascriptBridge.callHandler('showTips', data, null); //显示原生toast
                }
            );
        }
        function showHtml() {
            document.getElementById("htmlContent").innerHTML = document.getElementsByTagName("html")[0].innerHTML;
        }
        //******************************************************************************************
        // JS端注册提供给Java端调用的接口
        //******************************************************************************************
        function bridgeLog(logContent) {
            document.getElementById("show").innerHTML = logContent;
        }
        // 调用注册方法
        function connectWebViewJavascriptBridge(callback) {
            if (window.WebViewJavascriptBridge) {
                callback(WebViewJavascriptBridge)
            } else {
                document.addEventListener(
                    'WebViewJavascriptBridgeReady',
                    function() {
                        callback(WebViewJavascriptBridge)
                    },
                    false
                );
            }
        }
        connectWebViewJavascriptBridge(function(bridge) {
            //注册默认的Handler
            bridge.init(function(data, responseCallback) {
                document.getElementById("show").innerHTML = ("【原生传过来的数据】" + data);
                if (responseCallback) { //这行代码意思是:如果有responseCallback这个方法,就执行以下逻辑
                    responseCallback("【你好,我是白乾涛,这是JS默认的Handler返回的数据】");
                }
            });
            //注册供原生调用的Handler
            bridge.registerHandler("showInHtml", function(data, responseCallback) {
                document.getElementById("show").innerHTML = ("【原生传过来的数据】" + data);
                if (responseCallback) {
                    responseCallback("【你好,我是白乾涛,这是JS名为showInHtml的Handler返回的数据】");
                }
            });
        })
</script>
</html>

DSBridge 简介

DSBridge-IOS
DSBridge-Android

三端易用的现代跨平台 Javascript bridge, 通过它,你可以在Javascript和原生之间同步或异步的调用彼此的函数.

特性

  • 跨平台:同时支持ios/android
  • 支持同步、异步调
  • 双向调用:js可以调用native, native可以调用js。
  • 三端易用:三端指ios 、android和前端
  • 支持进度回调:一次调用,多次返回
  • Android支持腾讯X5内核
  • 支持以类的方式集中统一管理API
  • 支持API命名空间
  • 支持调试模式
  • 支持API存在性检测
  • 支持Javascript关闭页面事件回调
  • 支持Javascript 模态/非模态对话框

DSBridge 官方是同时支持 ios/android 的,WebViewJavascriptBridge 官方说明是支持 ios/osx 的,但 并不支持android , 当然,由于 WebViewJavascriptBridge 的人气 实在太高,也有一些人在 android 上实现了兼容的版本,如 gzsll/WebViewJavascriptBridge,但是总的来说,并非一家之作,这可能会给日后维护带来问题。

到目前为止,据作者所知,跨平台的 js bridge 中,DSBridge 是唯一一个支持同步调用的!

使用

1、添加依赖

allprojects {
    repositories {
        maven { url 'https://jitpack.io' }
    }
}
implementation 'com.github.wendux:DSBridge-Android:3.0-SNAPSHOT'
//或
implementation 'com.github.wendux:DSBridge-Android:x5-3.0-SNAPSHOT' //支持X5内核

2、原生定义供JS调用的API

public class JsApi {
    @JavascriptInterface
    public String testSyn(Object msg) {
        return msg + "[syn call]"; //同步API
    }

    @JavascriptInterface
    public void testAsyn(Object msg, CompletionHandler<String> handler) {
        handler.complete(msg + " [ asyn call]"); //异步API
    }
}

3、JS定义供原生调用的API

//cdn方式引入初始化代码(中国地区慢,建议下载到本地工程)
//<script src="https://unpkg.com/dsbridge@3.1.3/dist/dsbridge.js" />

//npm方式安装初始化代码
//npm install dsbridge@3.1.3

var dsBridge=require("dsbridge")

//注册 javascript API
dsBridge.register('addValue',function(l,r){
   return l+r;
})

4、JS调用原生API

var str=dsBridge.call("testSyn","testSyn"); //同步调用
dsBridge.call("testAsyn","testAsyn", function (v) {
  alert(v); //异步调用
})

5、原生调用JS API

DWebView dwebView= (DWebView) findViewById(R.id.dwebview);
dwebView.addJavascriptObject(new JsApi(), null); //Object object, String namespace
dwebView.callHandler("addValue", new Object[]{3, 4}, new OnReturnValue<Integer>() {
    @Override
    public void onValue(Integer retValue) {
        Log.d("jsbridge", "call succeed, return value is " + retValue);
    }
});

以上

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值