H5与原生通讯之二 (DSBrige,H5,IOS,Android源码实例)

H5与原生通讯之二 (DSBrige,H5,IOS,Android源码实例)

  • H5和natvie通讯方式简介
    • 1.H5和IOS通讯
    • 2.H5和Android通讯
  • H5和natvie通讯具体实现
    • 1.通过DSBrige方式
      • 1.1 H5和IOS 端源码解析
      • 1.2 dbbridge 安卓端源码解析
      • 1.3 dbbrige js 代码
      • 1.4 IOS端使用DSBridge框架API
          • 1.4.1 集成源码
          • 1.4.2 Api使用
          • 1.4.3 Api注意事项
          • 1.4.4 Api使用swift版实例
      • 1.5 Android端使用DSBridge框架API
    • 2.通过JSBrige方式
      • 2.1 H5和IOS
      • 2.2 H5和android
      • 2.3 JS端
        • 2.3.1 jsbridge流程
        • 2.3.2 js核心代码
      • 2.4

H5和natvie通讯方式简介

1.H5和IOS通讯

IOS主要有三种方式:WKWebView 、UIWebView、JavaScriptCoreWKWebView:

  • WKWebView:WKWebView优点很多,支持更多H5特性,刷新效率及内置手势等,更加强大,性能也更优,不一一列举,如果大家app不需要兼容7及以下版本,不需要拦截一些请求,直接解析本地一些文件,建议使用WKWebView。
  • UIWebView:较老webview,第一代。其中stringByEvaluatingJavaScriptFromString方法提供了OC与js交互的能力。JavaScriptCore(ios7及以后版本)。
  • JavaScriptCore:JavaScriptCore框架是webkit重要组成部分,主要是对JS进行解析和提供执行环境,Javascript的虚拟机,有点类似v8引擎。

2.H5和Android通讯

  • Android调用JS代码的方法主要有2种: WebView的loadUrlWebView的evaluateJavascript
  • JS调用Android代码的方法主要有3种
  1. WebView的addJavascriptInterface进行对象映射(低版本Android4以下好像有一些安全问题,本人没有验证)
  2. WebViewClient 的 shouldOverrideUrlLoading 方法回调拦截 url
  3. WebChromeClient 的onJsAlert、onJsConfirm、onJsPrompt方法回调拦截JS对话框alert()、confirm()、prompt() 消息一般常用onJsPrompt、prompt进行回调拦截。

H5和natvie通讯具体实现

1.通过DSBrige方式

  • DSBirdge-iOS 源码下载:DSBirdge-iOS
  • DSBirdge-Android源码下载:DSBirdge-Android

1.1 H5和IOS 端源码解析

  • 初始化
  1. 首先通过js代码注入为window添加_dswk用于标注是Native使用。
WKUserScript *script = [[WKUserScript alloc] initWithSource:@"window._dswk=true;"
                                              injectionTime:WKUserScriptInjectionTimeAtDocumentStart
                                           forMainFrameOnly:YES];
[configuration.userContentController addUserScript:script];

  1. 注册本地Native类并添加相应的namespacejavaScriptNamespaceInterfacesFDInternalApis类注册了部分需要的方法,namespace_dsb
FDInternalApis *  interalApis= [[FDInternalApis alloc] init];
interalApis.webview=self;
[self addJavascriptObject:interalApis namespace:@"_dsb"];

if(object!=NULL){
   
    [javaScriptNamespaceInterfaces setObject:object forKey:namespace];
    NSLog(javaScriptNamespaceInterfaces.description);
}
  • Native 调用 js
  1. Native调用js使用方法:
[dwebview callHandler:@"addValue" arguments:@[@3,@4] completionHandler:^(NSNumber * value){
   
    NSLog(@"%@",value);
}];
  1. js中需要注册相应方法(同步):
dsBridge.register('addValue', function (r, l) {
   
    return r + l;
})
  1. 对于异步js中的注册方式:
dsBridge.registerAsyn('append',function(arg1,arg2,arg3,responseCallback){
   
     responseCallback(arg1+" "+arg2+" "+arg3);
})
  1. native端使用时会走以下方法:
NSString * json=[FDWebUtil objToJsonString:@{
   @"method":info.method,@"callbackId":info.id,
                                                 @"data":[FDWebUtil objToJsonString: info.args]}];
[self evaluateJavaScript:[NSString stringWithFormat:@"window._handleMessageFromNative(%@)",json]
           completionHandler:nil];
  1. js会去调用_handleMessageFromNative方法并对数据进行解析。 当js处理完毕会调用:
bridge.call("_dsb.returnValue", ret)
  1. 然后native会取出保存在handerMap的block执行:
- (id) returnValue:(NSDictionary *) args{
   
    void (^ completionHandler)(NSString *  _Nullable)= handerMap[args[@"id"]];
    if(completionHandler){
   
        if(isDebug){
   
            completionHandler(args[@"data"]);
        }else{
   
            @try{
   
                completionHandler(args[@"data"]);
            }@catch (NSException *e){
   
                NSLog(@"%@",e);
            }
        }
        if([args[@"complete"] boolValue]){
   
            [handerMap removeObjectForKey:args[@"id"]];
        }
    }
    return nil;
}
  • js 调用 Native
  1. js 先定义一个:
var str=dsBridge.call("testSyn","testSyn");
  1. 然后会执行到
ret = prompt("_dsbridge=" + method, arg);
  1. 通过prompt在iOS端会调用代理方法
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler;
  1. 可以看到其处理方式:
NSString * prefix=@"_dsbridge=";
if ([prompt hasPrefix:prefix])
{
   
    NSString *method= [prompt substringFromIndex:[prefix length]];
    NSString *result=nil;
    if(isDebug){
   
        result =[self call:method :defaultText ];
    }else{
   
        @try {
   
            result =[self call:method :defaultText ];
        }@catch(NSException *exception){
   
            NSLog(@"%@", exception);
        }
    }
    completionHandler(result);
}
  1. 这里会调用一个关键方法
-(NSString *)call:(NSString*) method :(NSString*) argStr
{
   
    NSArray *nameStr=[FDWebUtil parseNamespace:[method stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];
    id JavascriptInterfaceObject= [javaScriptNamespaceInterfaces  valueForKey:nameStr[0]];
    NSString *error=[NSString stringWithFormat:@"Error! \n Method %@ is not invoked, since there is not a implementation for it",method];
    NSMutableDictionary*result =[NSMutableDictionary dictionaryWithDictionary:@{
   @"code":@-1,@"data":@""}];
    if(!JavascriptInterfaceObject){
   
        NSLog(@"Js bridge  called, but can't find a corresponded JavascriptObject , please check your code!");
    }else{
   
        method=nameStr[1];
        NSString *methodOne = [FDWebUtil methodByNameArg:1 selName:method class:[JavascriptInterfaceObject class]];
        NSString *methodTwo = [FDWebUtil methodByNameArg:2 selName:method class:[JavascriptInterfaceObject class]];
        SEL sel=NSSelectorFromString(methodOne);
        SEL selasyn=NSSelectorFromString(methodTwo);
        NSDictionary * args=[FDWebUtil jsonStringToObject:argStr];
        NSString *arg = [args safeObjectForKey:@"data"];
        NSString * cb;
        do{
   
            if(args && (cb= args[@"_dscbstub"])){
   
                if([JavascriptInterfaceObject respondsToSelector:selasyn]){
   
                    void (^completionHandler)(id,BOOL) = ^(id value,BOOL complete){
   
                        NSString *del=@"";
                        result[@"code"]=@0;
                        if(value!=nil){
   
                            result[@"data"]=value;
                        }
                        value=[FDWebUtil objToJsonString:result];
                        value=[value stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
                        
                        if(complete){
   
                            del=[@"delete window." stringByAppendingString:cb];
                        }
                        NSString*js=[NSString stringWithFormat:@"try {%@(JSON.parse(decodeURIComponent(\"%@\")).data);%@; } catch(e){};",cb,(value == nil) ? @"" : value,del];
                        
                        @synchronized(self)
                        {
   
                            UInt64  t=[[NSDate date] timeIntervalSince1970]*1000;
                            self->jsCache=[self->jsCache stringByAppendingString:js];
                            if(t-self->lastCallTime<50){
   
                                if(!self->isPending){
   
                                    [self evalJavascript:50];
                                    self->isPending=true;
                                }
                            }else{
   
                                [self evalJavascript:0];
                            }
                        }
                        
                    };
                    SuppressPerformSelectorLeakWarning(
                                                       [JavascriptInterfaceObject performSelector:selasyn withObject:arg withObject:completionHandler];
                                                       
                                                       );
                    
                    break;
                }
            }else if([JavascriptInterfaceObject respondsToSelector:sel]){
   
                id ret;
                SuppressPerformSelectorLeakWarning(
                                                   ret=[JavascriptInterfaceObject performSelector:sel withObject:arg];
                                                   );
                
                [result setValue:@0 forKey:@"code"];
                if(ret!=nil){
   
                    [result setValue:ret forKey:&#

你可能感兴趣的:(HTML5,H5和原生)