http://www.web008.net

JSBridge实现示例,冒泡排序

Python实现

import random

a=[random.randint(1,999) for x in range(0,33)]

# 冒泡排序,python中数组是按引用传递的,会直接在原数组上修改
def bubbleSort(array):
    flag = True
    i=0

    while i<len(array) and flag:
        j=len(array)-1
     flag = False

        while j>i:
            if array[j-1] > array[j]:
                array[j-1],array[j] = array[j],array[j-1]
                flag = True
            j=j-1

        i = i + 1

bubbleSort(a)
print(a)

 

说明

JSBridge实现示例

PHP实现

<?php
$a = [];

for($i=0;$i<36;$i++){
    array_push($a,mt_rand(1,999));
}
shuffle($a);

function bubbleSort(&$a){
    $flag = true;

    for($i = 0;$i<count($a) && $flag;$i++){
        $flag = false;

        for($j = count($a)-1;$j>$i;$j--){
            if($a[$j-1] > $a[$j]){
                $a[$j] += $a[$j-1];
                $a[$j-1] = $a[$j]-$a[$j-1];
                $a[$j] -= $a[$j-1];
                $flag = true;
            }
        }

    }
}

bubbleSort($a);

 

目录

  • 前言
    • 参考来源
    • 楔子
  • JS实现部分
    • 说明
    • 实现
  • Android实现部分
    • 说明
    • JSBridge类 实现
    • Callback类 实现
    • Webview容器关键代码 实现
    • API 类实现
  • iOS实现部分
    • 说明
    • WebViewJavascriptBridgeBase 实现
    • WebViewJavascriptBridge 实现
    • Webview容器关键代码 实现

前言

参考来源

前人栽树,后台乘凉,本文参考了以下来源

  • Hybrid APP架构设计思路
  • marcuswestin/WebViewJavascriptBridge

楔子

美高梅163888,本文介绍JSBridge的完整实现,包括JS部分,Android原生,iOS原生部分

JS实现部分

说明

这是一份剔除了业务之后的JSbridge实现代码(JS部分)。JS实现代码就一套

实现

实现代码如下

(function() {
    (function() {
        var hasOwnProperty = Object.prototype.hasOwnProperty;
        var JSBridge = window.JSBridge || (window.JSBridge = {});
        //jsbridge协议定义的名称
        var CUSTOM_PROTOCOL_SCHEME = 'CustomJSBridge';
        //最外层的api名称
        var API_Name = 'namespace_bridge';
        //进行url scheme传值的iframe
        var messagingIframe = document.createElement('iframe');
        messagingIframe.style.display = 'none';
        messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + API_Name;
        document.documentElement.appendChild(messagingIframe);

        //定义的回调函数集合,在原生调用完对应的方法后,会执行对应的回调函数id
        var responseCallbacks = {};
        //唯一id,用来确保每一个回调函数的唯一性
        var uniqueId = 1;
        //本地注册的方法集合,原生只能调用本地注册的方法,否则会提示错误
        var messageHandlers = {};
        //当原生调用H5注册的方法时,通过回调来调用(也就是变为了异步执行,加强安全性)
        var dispatchMessagesWithTimeoutSafety = true;
        //本地运行中的方法队列
        var sendMessageQueue = [];

        //实际暴露给原生调用的对象
        var Inner = {
            /**
             * @description 注册本地JS方法通过JSBridge给原生调用
             * 我们规定,原生必须通过JSBridge来调用H5的方法
             * 注意,这里一般对本地函数有一些要求,要求第一个参数是data,第二个参数是callback
             * @param {String} handlerName 方法名
             * @param {Function} handler 对应的方法
             */
            registerHandler: function(handlerName, handler) {
                messageHandlers[handlerName] = handler;
            },
            /**
             * @description 调用原生开放的方法
             * @param {String} handlerName 方法名
             * @param {JSON} data 参数
             * @param {Function} callback 回调函数
             */
            callHandler: function(handlerName, data, callback) {
                //如果没有 data
                if(arguments.length == 3 && typeof data == 'function') {
                    callback = data;
                    data = null;
                }
                _doSend({
                    handlerName: handlerName,
                    data: data
                }, callback);
            },
            /**
             * iOS专用
             * @description 当本地调用了callHandler之后,实际是调用了通用的scheme,通知原生
             * 然后原生通过调用这个方法来获知当前正在调用的方法队列
             */
            _fetchQueue: function() {
                var messageQueueString = JSON.stringify(sendMessageQueue);
                sendMessageQueue = [];
                return messageQueueString;
            },
            /**
             * @description 原生调用H5页面注册的方法,或者调用回调方法
             * @param {String} messageJSON 对应的方法的详情,需要手动转为json
             */
            _handleMessageFromNative: function(messageJSON) {
                setTimeout(_doDispatchMessageFromNative);
                /**
                 * @description 处理原生过来的方法
                 */
                function _doDispatchMessageFromNative() {
                    var message;
                    try {
                        message = JSON.parse(messageJSON);
                    } catch(e) {
                        //TODO handle the exception
                        console.error("原生调用H5方法出错,传入参数错误");
                        return;
                    }

                    //回调函数
                    var responseCallback;
                    if(message.responseId) {
                        //这里规定,原生执行方法完毕后准备通知h5执行回调时,回调函数id是responseId
                        responseCallback = responseCallbacks[message.responseId];
                        if(!responseCallback) {
                            return;
                        }
                        //执行本地的回调函数
                        responseCallback(message.responseData);
                        delete responseCallbacks[message.responseId];
                    } else {
                        //否则,代表原生主动执行h5本地的函数
                        if(message.callbackId) {
                            //先判断是否需要本地H5执行回调函数
                            //如果需要本地函数执行回调通知原生,那么在本地注册回调函数,然后再调用原生
                            //回调数据有h5函数执行完毕后传入
                            var callbackResponseId = message.callbackId;
                            responseCallback = function(responseData) {
                                //默认是调用EJS api上面的函数
                                //然后接下来原生知道scheme被调用后主动获取这个信息
                                //所以原生这时候应该会进行判断,判断对于函数是否成功执行,并接收数据
                                //这时候通讯完毕(由于h5不会对回调添加回调,所以接下来没有通信了)
                                _doSend({
                                    handlerName: message.handlerName,
                                    responseId: callbackResponseId,
                                    responseData: responseData
                                });
                            };
                        }

                        //从本地注册的函数中获取
                        var handler = messageHandlers[message.handlerName];
                        if(!handler) {
                            //本地没有注册这个函数
                        } else {
                            //执行本地函数,按照要求传入数据和回调
                            handler(message.data, responseCallback);
                        }
                    }
                }
            }

        };
        /**
         * @description JS调用原生方法前,会先send到这里进行处理
         * @param {JSON} message 调用的方法详情,包括方法名,参数
         * @param {Function} responseCallback 调用完方法后的回调
         */
        function _doSend(message, responseCallback) {
            if(responseCallback) {
                //取到一个唯一的callbackid
                var callbackId = Util.getCallbackId();
                //回调函数添加到集合中
                responseCallbacks[callbackId] = responseCallback;
                //方法的详情添加回调函数的关键标识
                message['callbackId'] = callbackId;
            }
            var uri;
            //android中,可以通过onJsPrompt或者截取Url访问都行
            var ua = navigator.userAgent;
            if(ua.match(/(iPhonesOS)s([d_]+)/)||ua.match(/(iPad).*OSs([d_]+)/)) {
                //ios中,通过截取客户端url访问
                //因为ios可以不暴露scheme,而是由原生手动获取
                //正在调用的方法详情添加进入消息队列中,原生会主动获取
                sendMessageQueue.push(message);
                uri = Util.getUri();
            }else{
                //android中兼容处理,将所有的参数一起拼接到url中
                uri = Util.getUri(message);
            }
            //获取 触发方法的url scheme
            //采用iframe跳转scheme的方法
            messagingIframe.src = uri;
        }

        var Util = {
            getCallbackId: function() {
                //如果无法解析端口,可以换为Math.floor(Math.random() * (1 << 30));
                return 'cb_' + (uniqueId++) + '_' + new Date().getTime();
            },
            //获取url scheme
            //第二个参数是兼容android中的做法
            //android中由于原生不能获取JS函数的返回值,所以得通过协议传输
            getUri: function(message) {
                var uri = CUSTOM_PROTOCOL_SCHEME + '://' + API_Name;
                if(message) {
                    //回调id作为端口存在
                    var callbackId, method, params;
                    if(message.callbackId) {
                        //第一种:h5主动调用原生
                        callbackId = message.callbackId;
                        method = message.handlerName;
                        params = message.data;
                    } else if(message.responseId) {
                        //第二种:原生调用h5后,h5回调
                        //这种情况下需要原生自行分析传过去的port是否是它定义的回调
                        callbackId = message.responseId;
                        method = message.handlerName;
                        params = message.responseData;
                    }
                    //参数转为字符串
                    params = this.getParam(params);
                    //uri 补充
                    uri += ':' + callbackId + '/' + method + '?' + params;
                }

                return uri;
            },
            getParam: function(obj) {
                if(obj && typeof obj === 'object') {
                    return JSON.stringify(obj);
                } else {
                    return obj || '';
                }
            }
        };
        for(var key in Inner) {
            if(!hasOwnProperty.call(JSBridge, key)) {
                JSBridge[key] = Inner[key];
            }
        }

    })();

    //注册一个测试函数
    JSBridge.registerHandler('testH5Func', function(data, callback) {
        alert('测试函数接收到数据:' + JSON.stringify(data));
        callback && callback('测试回传数据...');
    });
    /*
     ***************************API********************************************
     * 开放给外界调用的api
     * */
    window.jsapi = {};
    /**
     ***app 模块 
     * 一些特殊操作
     */
    jsapi.app = {
        /**
         * @description 测试函数
         */
        testNativeFunc: function() {
            //调用一个测试函数
            JSBridge.callHandler('testNativeFunc', {}, function(res) {
                callback && callback(res);
            });
        }
    };
})();               

Android实现部分

说明

这是Android原生中配套的JSBridge实现代码。Android的实现相对比JS复杂,包括多个部分

JSBridge类实现

实现代码如下

public class JSBridge {
    private static Map<String, HashMap<String, Method>> exposedMethods = new HashMap<>();

    //原生注册API方法
    public static void register(String exposedName, Class<? extends IBridge> clazz) {
        if (!exposedMethods.containsKey(exposedName)) {
            try {
                exposedMethods.put(exposedName, getAllMethod(clazz));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    //得到所有的注册方法
    private static HashMap<String, Method> getAllMethod(Class injectedCls) throws Exception {
        HashMap<String, Method> mMethodsMap = new HashMap<>();
        Method[] methods = injectedCls.getDeclaredMethods();
        for (Method method : methods) {
            String name;
            if (method.getModifiers() != (Modifier.PUBLIC | Modifier.STATIC) || (name = method.getName()) == null) {
                continue;
            }
            Class[] parameters = method.getParameterTypes();
            if (null != parameters && parameters.length == 4) {
                if (parameters[0] == BaseWebLoader.class && parameters[1] == WebView.class && parameters[2] == JSONObject.class && parameters[3] == Callback.class) {
                    mMethodsMap.put(name, method);
                }
            }
        }
        return mMethodsMap;
    }

    //调用Hava中的方法
    //其中BaseWebLoader是JSBridge的webview容器(二次封装)
    //执行完方法后,如果又回到,自动就会调用
    public static String callJava(BaseWebLoader webLoader,WebView webView, String uriString) {
        String methodName = "";
        String className = "";
        String param = "{}";
        String port = "";
        if (!TextUtils.isEmpty(uriString) && uriString.startsWith("EpointJSBridge")) {
            Uri uri = Uri.parse(uriString);
            className = uri.getHost();
            param = uri.getQuery();
            port = uri.getPort() + "";
            String path = uri.getPath();
            if (!TextUtils.isEmpty(path)) {
                methodName = path.replace("/", "");
            }
        }


        if (exposedMethods.containsKey(className)) {
            HashMap<String, Method> methodHashMap = exposedMethods.get(className);

            if (methodHashMap != null && methodHashMap.size() != 0 && methodHashMap.containsKey(methodName)) {
                Method method = methodHashMap.get(methodName);
                if (method != null) {
                    try {
                        method.invoke(null,webLoader, webView, new JSONObject(param), new Callback(webView, port));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return null;
    }
}   

这个类的作用是原生定义一些暴露的api

Callback类实现

实现代码如下

public class Callback {
    private static Handler mHandler = new Handler(Looper.getMainLooper());
    private static final String CALLBACK_JS_FORMAT = "javascript:JSBridge._handleMessageFromNative(%s);";
    private String mPort;
    private WeakReference<WebView> mWebViewRef;

    public Callback(WebView view, String port) {
        mWebViewRef = new WeakReference<>(view);
        mPort = port;
    }


    public void apply(JSONObject jsonObject) throws JSONException {
        JSONObject object = new JSONObject();
        object.put("responseId", mPort);
        object.putOpt("responseData", jsonObject);
        final String execJs = String.format(CALLBACK_JS_FORMAT, String.valueOf(object));
        //如果activity已经关闭则不回调
        if (mWebViewRef != null && mWebViewRef.get() != null && !((BaseWebLoader) mWebViewRef.get().getContext()).getActivity().isFinishing()) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mWebViewRef.get().loadUrl(execJs);
                }
            });
        }
    }
}

这个类的作用是,定义原生中的回调函数

郑重声明:本文版权归美高梅163888所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。