美文网首页
二:iOS - dsBridge.js源码备注

二:iOS - dsBridge.js源码备注

作者: 林鹏_dev | 来源:发表于2021-07-16 14:06 被阅读0次

主要是用于理解,非专业JS开发,错误之处务必不吝指出

dsBridge.js

//定义一个bridge对象
var bridge = {
    default:this,// for typescript
    /*
    bridge对象声明一个call方法: js的参数可以自动缺省匹配(很强)
    如果cb传的是一个方法的话,则该是异步调用
    
    同步:
    dsBridge.call("testSyn", "Hello")
   
    异步:
     dsBridge.call("testAsyn","hello", function (v) {
            alert(v)
     })

    */
    call: function (method, args, cb) {
        var ret = '';
        if (typeof args == 'function') {
            cb = args;
            args = {};
        }
        var arg={data:args===undefined?null:args}//定义arg对象
        //异步(通过传方法来实现异步)
        if (typeof cb == 'function') { //如果cb参数是一个方法
            var cbName = 'dscb' + window.dscb++; //cbName = dscb1、dscb2、dscb3...
            window[cbName] = cb; //window.dscb1 = cb,因此可以调用 dscb1(v)、dscb2(v)等函数,对应 function callAsyn() {
//                                                                                            dsBridge.call("testAsyn","testAsyn", function (v) {
//                                                                                                alert(v)
//                                                                                            })
//                                                                                        }   中的 function(v){}
            arg['_dscbstub'] = cbName; //arg对象中添加一个属性 _dscbstub = cbName
        }
        arg = JSON.stringify(arg) //arg转为json

        //if in webview that dsBridge provided, call!
        if(window._dsbridge){//是否注入过 _dsbridge对象
            //调用已有的_dsbridge的call()方法
           ret=  _dsbridge.call(method, arg) 
        }else if(window._dswk||navigator.userAgent.indexOf("_dsbridge")!=-1){//如果注入过_dswk对象,或者 userAgent的最后是_dsbridge
            //通过prompt进行通信(客户端的runJavaScriptTextInputPanelWithPrompt协议里面会收到:_dsbridge=xxx 和 arg参数)
            ret = prompt("_dsbridge=" + method, arg); 
        }

       return  JSON.parse(ret||'{}').data//数据格式转化
    },

    /*
    注册方法给客户端调用
    [self callHandler:@"_hasJavascriptMethod" arguments:@[handlerName] completionHandler:^(NSNumber* _Nullable value) {
        callback([value boolValue]);
    }];

    */
    register: function (name, fun, asyn) {
        //根据asyn确定同步还是异步(默认是同步调用)
        var q = asyn ? window._dsaf : window._dsf
        if (!window._dsInit) {
            window._dsInit = true;
            //notify native that js apis register successfully on next event loop
           
            //延迟0s执行 - 确保bridge未实例化而导致调用失败(同步执行有风险)
            setTimeout(function () {
                bridge.call("_dsb.dsinit");
            }, 0)
        }
        //将注册给native调用方法对应放到_dsaf|_dsf容器里面
        if (typeof fun == "object") {
            q._obs[name] = fun;
        } else {
            q[name] = fun
        }
    },
    /*
    内置事件:
    dsBridge.registerAsyn('append', function (arg1, arg2, arg3, responseCallback) {
        responseCallback(arg1 + " " + arg2 + " " + arg3);
    })
    */
    registerAsyn: function (name, fun) {
        this.register(name, fun, true);
    },
    /*
    内置事件:
    dsBridge.hasNativeMethod(name)
    */
    hasNativeMethod: function (name, type) {
        return this.call("_dsb.hasNativeMethod", {name: name, type:type||"all"});
    },
    /*
    内置事件:
    
    */
    disableJavascriptDialogBlock: function (disable) {
        this.call("_dsb.disableJavascriptDialogBlock", {
            disable: disable !== false
        })
    }
};

//立即执行函数
!function () {
    //防止重复注入
    if (window._dsf) return;
    var _close=window.close;
    //申明一个ob对象
    var ob = {
        //保存JS同步方法
        _dsf: {
            _obs: {}
        },
        //保存JS异步方法
        _dsaf: {
            _obs: {}
        },
        dscb: 0,
        dsBridge: bridge,
        //注入bridge对象:属性名为:dsBridge
        dsBridge: bridge,
        close: function () {
            //_dsb是跟iOS前端约定好的内置命名空间事件:[self addJavascriptObject:interalApis namespace:@"_dsb"];
            if(bridge.hasNativeMethod('_dsb.closePage')){
             bridge.call("_dsb.closePage")
            }else{
             _close.call(window)
            }
        },
        /*
        oc调用同步js的addValue方法
        // namespace syn test
        [dwebview callHandler:@"syn.addValue" arguments:@[@55,@6] completionHandler:^(NSDictionary * _Nullable value) {
             NSLog(@"Namespace syn.addValue(5,6): %@",value);
        }];

         客户端调用js方法
         NSString * json=[JSBUtil objToJsonString:@{@"method":info.method,@"callbackId":info.id,
                                               @"data":[JSBUtil objToJsonString: info.args]}];
         [self evaluateJavaScript:[NSString stringWithFormat:@"window._handleMessageFromNative(%@)",json]
           completionHandler:nil];

         eg: 同步:json =  {"callbackId":0,"method":"syn.addValue","data":"[5,6]"}
             异步:json =  {"callbackId":0,"method":"asyn.addValue","data":"[5,6]"}
        */
        _handleMessageFromNative: function (info) {
            var arg = JSON.parse(info.data);
            var ret = {
                id: info.callbackId,//客户端传一个callbackId后续回传给客户端方便从字典里面获取对应的block
                complete: true
            }
            var f = this._dsf[info.method];
            var af = this._dsaf[info.method]
            var callSyn = function (f, ob) {
                //f劫持ob对象的方法和属性,传递arg参数
                ret.data = f.apply(ob, arg)//等价于 ret.data = addValue(5,6) 即:ret.data = 11
                //将结果再传给native
                bridge.call("_dsb.returnValue", ret)
            }
            var callAsyn = function (f, ob) {
                //arg追加参数(追加方法实现异步)
                arg.push(function (data, complete) {
                    ret.data = data;
                    ret.complete = complete!==false;
                    bridge.call("_dsb.returnValue", ret)
                })
                f.apply(ob, arg)
            }
            if (f) {
                callSyn(f, this._dsf);
            } else if (af) {
                callAsyn(af, this._dsaf);
            } else {
                //with namespace
                var name = info.method.split('.');
                if (name.length<2) return;
                //获取方法名字
                var method=name.pop();//取最后一个元素:方法名
                var namespace=name.join('.')
                var obs = this._dsf._obs;
                var ob = obs[namespace] || {};
                //obs容器里面获取方法
                var m = ob[method];
                //如果在同步的容器里面找到该方法 则同步执行
                if (m && typeof m == "function") {
                    callSyn(m, ob);
                    return;
                }
                //接着在异步的容器里面找方法 找到则执行异步
                obs = this._dsaf._obs;
                ob = obs[namespace] || {};
                m = ob[method];
                if (m && typeof m == "function") {
                    callAsyn(m, ob);
                    return;
                }
            }
        }
    }
    //将全部的属性赋值给window, 这样h5就可以通过window._dsf,window.dsBridge来访问,默认可以不写window. 直接_dsf或者dsBridge就可以访问
    for (var attr in ob) {
        window[attr] = ob[attr]
    }
    
    /*
    立即调用一个_hasJavascriptMethod方法 给客户端调用
    [self callHandler:@"_hasJavascriptMethod" arguments:@[handlerName] completionHandler:^(NSNumber* _Nullable value) {
        callback([value boolValue]);
    }];

    */

    bridge.register("_hasJavascriptMethod", function (method, tag) {
         var name = method.split('.')
         //命名空间是通过类似倒置域名的方式来实现:比如上面的 bridge.call("_dsb.dsinit");

         //没有命名空间 则method就是方法名:dsinit
         if(name.length<2) {
           return !!(_dsf[name]||_dsaf[name])
         }else{
           // with namespace
           var method=name.pop()//取最后一个元素:dsinit
           var namespace=name.join('.')
           //容器里面获取是否已注册方法
           var ob=_dsf._obs[namespace]||_dsaf._obs[namespace]
           return ob&&!!ob[method]
         }
    })
}();

相关文章

网友评论

      本文标题:二:iOS - dsBridge.js源码备注

      本文链接:https://www.haomeiwen.com/subject/cknvpltx.html