Android混合开发(一)——移动端与前端交互之JSBridge引入

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/DT235201314/article/details/83314866

一丶概述

最近转前端,在做一个混合项目,Android + 前端Vue技术,Vue没那么快上手,先分享一下Android部分的经验,后期会学习Flutter,和RN,边学边做边分享

二丶效果演示

三丶JSBridge引入

1.什么是JSBridge

JSBridge是移动UIView和Html交互通信的桥梁,就是实现java(ios为oc)和js的互相调用的桥梁。出于安全考虑,android自4.4后不建议使用@JavascriptInterface注解,所以Android4.4以后就没有了默认的Jsbridge的实现,替代了WebView的自带JavascriptInterface的接口,使得我们的开发更加灵活和安全。

说明:

(1).JSBridge作用是移动端和前端通信;

(2).Java  oc  js要分别实现;

(3).替代JavascriptInterface的接口,安全;

2.JSBridge集成

GitHub全局搜索JSBridge

star最多,Android端一般会选择上面一个,iOS端会选择下面一个

这样前端的同事就得注意了,两者初始化方法不一样,需要分别对接解决兼容问题

当然也有大牛解决这个问题

DSBridge

这里由于跟iOS没达成一致选择了上面的方式

四丶效果实现

效果作用,演示Android与前端方法互相调用

1.添加依赖

repositories {
    // ...
    maven { url "https://jitpack.io" }
}

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

2.布局页面

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:id="@+id/et_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入测试数据"/>

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="调用js方法"/>

    <com.github.lzyzsd.jsbridge.BridgeWebView
        android:id="@+id/JsBridgeWebView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>


</LinearLayout>

3.Android端桥注册及调用

public class MixedActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.mixed_activity);
        Button btn = (Button) findViewById(R.id.btn);
        final EditText etText = (EditText) findViewById(R.id.et_text);
        final BridgeWebView bridgeWebView = (BridgeWebView) findViewById(R.id.JsBridgeWebView);
        bridgeWebView.setDefaultHandler(new DefaultHandler());
        bridgeWebView.setWebChromeClient(new WebChromeClient());
        bridgeWebView.loadUrl("file:///android_asset/a.html");

        /**
         * js调用Android
         *
         *  参数一:getUserInfo就是注册供JS调用的方法名,
         *  参数二:data是JS传过来的参数,
         *  参数三:CallBackFunction 函数中需要把JS需要的response返回给JS
         */
        bridgeWebView.registerHandler("submitFromWeb", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                Log.e("TAG", "js返回:" + data);
                //显示js传递给Android的消息
                Toast.makeText(MixedActivity.this, "js返回:" + data, Toast.LENGTH_LONG).show();
                //Android返回给JS的消息
                function.onCallBack("我是js调用Android返回数据:" + etText.getText().toString());
            }
        });


        /**
         * Android调用js
         *
         * 参数一:js中的方法名称
         * 参数二:Android传递给js数据
         * 参数三:回调接口,data为Android调用js方法的返回数据
         */
        btn.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                bridgeWebView.callHandler("functionInJs", "Android调用js的方法", new CallBackFunction() {
                    @Override
                    public void onCallBack(String data) {
                        Log.e("TAG", "onCallBack:" + data);
                        Toast.makeText(MixedActivity.this, data, Toast.LENGTH_LONG).show();
                    }
                });
            }
        });
    }
}

注意点:1.桥名称相同;2.传递数据类型相同(android只能String,所以统一String)

4.前端JSBridge实现

这里要做重点注意,这边开发由于是iOS与前端先调试完,再调试Android,方法各种调不通,明明参考的同一篇博客,就是无效。原因呢:注册的方式不对,与iOS的方式只有细节上的不同。

丢给前端一个文件,分别做兼容,才解决问题

WebViewJavascriptBridge.js

//notation: js file can only use this kind of comments
//since comments will cause error when use in webview.loadurl,
//comments will be remove by java use regexp
(function() {
    if (window.WebViewJavascriptBridge) {
        return;
    }

    var messagingIframe;
    var sendMessageQueue = [];
    var receiveMessageQueue = [];
    var messageHandlers = {};

    var CUSTOM_PROTOCOL_SCHEME = 'yy';
    var QUEUE_HAS_MESSAGE = '__QUEUE_MESSAGE__/';

    var responseCallbacks = {};
    var uniqueId = 1;

    function _createQueueReadyIframe(doc) {
        messagingIframe = doc.createElement('iframe');
        messagingIframe.style.display = 'none';
        doc.documentElement.appendChild(messagingIframe);
    }

    //set default messageHandler
    function init(messageHandler) {
        if (WebViewJavascriptBridge._messageHandler) {
            throw new Error('WebViewJavascriptBridge.init called twice');
        }
        WebViewJavascriptBridge._messageHandler = messageHandler;
        var receivedMessages = receiveMessageQueue;
        receiveMessageQueue = null;
        for (var i = 0; i < receivedMessages.length; i++) {
            _dispatchMessageFromNative(receivedMessages[i]);
        }
    }

    function send(data, responseCallback) {
        _doSend({
            data: data
        }, responseCallback);
    }

    function registerHandler(handlerName, handler) {
        messageHandlers[handlerName] = handler;
    }

    function callHandler(handlerName, data, responseCallback) {
        _doSend({
            handlerName: handlerName,
            data: data
        }, responseCallback);
    }

    //sendMessage add message, 触发native处理 sendMessage
    function _doSend(message, responseCallback) {
        if (responseCallback) {
            var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
            responseCallbacks[callbackId] = responseCallback;
            message.callbackId = callbackId;
        }

        sendMessageQueue.push(message);
        messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
    }

    // 提供给native调用,该函数作用:获取sendMessageQueue返回给native,由于android不能直接获取返回的内容,所以使用url shouldOverrideUrlLoading 的方式返回内容
    function _fetchQueue() {
        var messageQueueString = JSON.stringify(sendMessageQueue);
        sendMessageQueue = [];
        //android can't read directly the return data, so we can reload iframe src to communicate with java
        messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString);
    }

    //提供给native使用,
    function _dispatchMessageFromNative(messageJSON) {
        setTimeout(function() {
            var message = JSON.parse(messageJSON);
            var responseCallback;
            //java call finished, now need to call js callback function
            if (message.responseId) {
                responseCallback = responseCallbacks[message.responseId];
                if (!responseCallback) {
                    return;
                }
                responseCallback(message.responseData);
                delete responseCallbacks[message.responseId];
            } else {
                //直接发送
                if (message.callbackId) {
                    var callbackResponseId = message.callbackId;
                    responseCallback = function(responseData) {
                        _doSend({
                            responseId: callbackResponseId,
                            responseData: responseData
                        });
                    };
                }

                var handler = WebViewJavascriptBridge._messageHandler;
                if (message.handlerName) {
                    handler = messageHandlers[message.handlerName];
                }
                //查找指定handler
                try {
                    handler(message.data, responseCallback);
                } catch (exception) {
                    if (typeof console != 'undefined') {
                        console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception);
                    }
                }
            }
        });
    }

    //提供给native调用,receiveMessageQueue 在会在页面加载完后赋值为null,所以
    function _handleMessageFromNative(messageJSON) {
        console.log(messageJSON);
        if (receiveMessageQueue && receiveMessageQueue.length > 0) {
            receiveMessageQueue.push(messageJSON);
        } else {
            _dispatchMessageFromNative(messageJSON);
        }
    }

    var WebViewJavascriptBridge = window.WebViewJavascriptBridge = {
        init: init,
        send: send,
        registerHandler: registerHandler,
        callHandler: callHandler,
        _fetchQueue: _fetchQueue,
        _handleMessageFromNative: _handleMessageFromNative
    };

    var doc = document;
    _createQueueReadyIframe(doc);
    var readyEvent = doc.createEvent('Events');
    readyEvent.initEvent('WebViewJavascriptBridgeReady');
    readyEvent.bridge = WebViewJavascriptBridge;
    doc.dispatchEvent(readyEvent);
})();

jsbridgewebview加载的html

<html>
<head>
    <meta content="text/html; charset=utf-8" http-equiv="content-type">
    <title>js调用java</title>
</head>
<body>
<p>
    <input type="text" id="text1" value="请输入测试数据" width="400px" height="200px"/>
</p>
<p>
    <input type="button" id="enter" value="调用安卓的方法" onclick="testClick();"
    />
</p>


<script>

         //js调用Android方法:接收Android传递过来的数据,并做处理

         function testClick() {

          //参数一:调用java中的方法   submitFromWeb是方法名,必须和Android中注册时候的方法名称保持一致
          //参数二:返回给Android端的数据,可以为字符串,json等信息
          //参数三:js接收到Android传递过来的数据之后的相应处理逻辑

            window.WebViewJavascriptBridge.callHandler(
               'submitFromWeb'
               , {'param': "JS成功接收到数据---"}
               , function(responseData) {
                    alert(responseData)
               }
           );
       }

       //JS注册事件监听
       function connectWebViewJavascriptBridge(callback) {
           if (window.WebViewJavascriptBridge) {
               callback(WebViewJavascriptBridge)
           } else {
               document.addEventListener(
                   'WebViewJavascriptBridgeReady'
                   , function() {
                       callback(WebViewJavascriptBridge)
                   },
                   false
               );
           }
       }

        //注册回调函数,第一次连接时调用 初始化函数
       connectWebViewJavascriptBridge(function(bridge) {
            //初始化
           bridge.init(function(message, responseCallback) {
               var data = {
                   'Javascript Responds': 'Wee!'
               };
               responseCallback(data);
           });


           //Android调用js方法:functionInJs方法名称需要保持一致 ,并返回给Android通知

           bridge.registerHandler("functionInJs", function(data, responseCallback) {
                alert(data);
               var data = document.getElementById("text1").value;
               var responseData = "我是Android调用js方法返回的数据---"+ data;
               responseCallback(responseData);
           });
       })

</script>

</body>
</html>

写在最后微信扫码提问

源码下载:

https://github.com/JinBoy23520/CoderToDeveloperByTCLer

如果文章对你有帮助,欢迎点赞关注

猜你喜欢

转载自blog.csdn.net/DT235201314/article/details/83314866
今日推荐