iOS-JSBrigde:JS和Native通信实现的两种方式

现在纯原生的APP是越来越少了,更多的都加入了Web混合开发(有UiWebView和iOS 8之后的WKWebView,后者在性能上优化了许多),甚至有些直接是WebAPP。JSPatch、weex以及ReactNative等热更新技术也相当流行,虽然2017年3月份苹果的一封邮件警告,让许多使用者对这些热更新技术有点害怕。
既然用到了web,就避免不了JS和OC语言的互相调用。
本文介绍自己项目中的js-oc互相调用实现方法。从一开始简单的url拦截到最近的基于JavaScriptcore建立的JSbrige两套方案。

URL拦截方式

JS调用OC:


- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    
    NSString * currentUrl = webView.request.URL.absoluteString;
    //判断是否是单击
    if (navigationType == UIWebViewNavigationTypeLinkClicked)
    {
        NSURL *url = [request URL];
        NSLog(@"当前url%@",url);
        NSString *urlStr = url.absoluteString;
        if ([[url scheme] isEqualToString:@"cloudsc6"]){
            if ([[url host] isEqualToString:@"PLAYVOICE"]) {
                NSLog(@"准备播放语音消息");
            }
        }
            else {
                return YES;
            }
    }
    return YES;
}

这个实现方式事实上有很多局限,首先参数的传递就不太友好,参数放在url中实现实在不是很优雅的实现方式。
而且,工作中实测,当JS实现事件的方式是Ajax异步的时候,有些事件这个代理是拦截不到的。
当时解决的方案是使用了NSURLProtocol全局去拦截url请求。如下

@implementation InnerResourcesURLProtocol
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    BOOL can = NO;
    //NSLog(@"--->can handled Request? #%lu: URL = %@", (unsigned long)requestCount++, request);
  //  NSLog(@"%@",request);
    if([NSURLProtocol propertyForKey:CLOUDSEE_INNER_PROTOCOL_REQ_HANDLED_KEY inRequest:request]){
        can =  NO;
    }else {
        NSURL *url = [request URL];
        if(([[url scheme] isEqualToString:DEFAULT_SCHEMA])||([[url scheme]isEqualToString:@"th"])){
            can = YES;
        }
    }
    return can;
}
- (void) startLoading
{
    AppDelegate *dd = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    NSURL *url = [self.request URL];
    if ([[url scheme] isEqualToString:@"PLAYVOICE"]) {
    }
}

同样地也有其局限性。

OC调用JS

-(void)webViewDidFinishLoad:(UIWebView *)webView
{
    NSString *scriptResult = [self.webView stringByEvaluatingJavaScriptFromString:@"didWebViewScroll()"];
    if([scriptResult isEqualToString:@"NO"]) {
        self.webView.scrollView.bounces=NO;
    }
}

这个实现方式简单,但是同样也相当不友好。首先传参数在didWebViewScroll()中的()实现,不仅对长度有限,而且如果参数有含有''会导致截取错误代码。

而且这个方案下,有些事件安卓能截取,iOS 不能截取。有些事件iOS 能截取,安卓不能截取。跨平台性也不好。

WebViewJavascriptBridge

网上有一个著名的库,星星数已达8823。但是其底层实现原理还是基于URL拦截方式去实现。
再次证明星星数不能代表什么,只能说其比较出名。

JavaScriptcore桥接实现

如果你的APP不需要支持iOS 7之前的版本,可以使用以下方案,我们公司现在用的就是这个方案。
苹果在iOS 7中加入了JavaScriptCore框架。该框架让Objective-C和JavaScript代码直接的交互变得更加的简单方便。

桥接关键 和JS调用OC

-(void)webViewDidFinishLoad:(UIWebView *)webView{
    self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    //打印JS异常
    [self.jsContext setExceptionHandler:^(JSContext *context, JSValue *value) {
        NSLog(@"oc catches the exception: %@", value);
    }];
    //以下是桥接JS与OC的关键
    JSModel * model = [[JSModel alloc]init];
    model.controller = self;
    model.JSContext = self.jsContext;
    //以下是H5中调用的方法名称
    NSString * key = @"DDJSBridge";
    [self.jsContext setObject:model forKeyedSubscript:key];
    JSValue * loadData = self.jsContext[@"loadData"];
    //以下是回调JS方法,可以传入封装好的参数
    [loadData callWithArguments:@[self.jsonStr]];
    if (isLoadData) {
        [self firstIn];
    }
    isLoadData = NO;
    // 以下是执行JS方法
    // [self.jsContext evaluateScript:self.htmlCont];
}

OC调用JS

只要将JSModel继承JSExport,再本地实现以下方法

-(void)call:(NSString *)methodName :(NSDictionary *)params :(JSValue *)callBack{
    NSDictionary * data = [params objectForKey:@"data"];
    if ([methodName isEqualToString:@"showShareButton"]) {
        NSString * iconsStrUrl = [data objectForKey:@"icon"];
        if (iconsStrUrl) {
            NSURL * iconUrl = [NSURL URLWithString:iconsStrUrl];
            NSData * imageData = [NSData dataWithContentsOfURL:iconUrl];
            UIImage *image = [UIImage imageWithData:imageData scale:3];
            UIBarButtonItem *rightBarButton = [[UIBarButtonItem alloc]
                                               initWithImage:image
                                               style:UIBarButtonItemStylePlain
                                               target:self
                                               action:@selector(rightClick)];
            self.controller.navigationItem.rightBarButtonItem = rightBarButton;
        }
}

至于跨平台性,也比第一个方案要友好,安卓也有类似的实现方案。

第二个方案的DEMO已经上传,可以下载
JSBrigeOC版本
JSBrigeSwift版本

JavascriptBridge实现内部原理

有一个百度团队的博客写的很好,可以参考。
深入浅出 JavaScriptCore
浅谈JavaScriptCore

阿里无限团队接口实现规范

我们也是基本按照它的接口规范的
阿里无限团队接口实现规范

Weex 是如何在 iOS 客户端上跑起来的

Weex 是如何在 iOS 客户端上跑起来的

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容

  • 用到的组件 1、通过CocoaPods安装 2、第三方类库安装 3、第三方服务 友盟社会化分享组件 友盟用户反馈 ...
    SunnyLeong阅读 14,598评论 1 180
  • 懂,是一种简简单单的感动,懂得你的眼泪,给你最暖的安慰;深知你的憔悴,怪你为何不知疲惫。 真正的懂得,不必言语,不...
    认真就好阅读 166评论 0 0
  • 快过年了啊 没有那张张色彩斑斓的年画 没有那灯笼里五颜六色的小洋腊 也没有发辫上欲飞的蝴蝶结了 一同没有了的 ...
    昭昭之辉阅读 290评论 5 22
  • 常看到,说十七八的那段时期爱上的人,会很难忘却。 与其说你爱着那个人,不如说你更爱那时勇敢的自己,即使是一份青涩的...
    Cllare1992阅读 155评论 0 0