使用javascriptcore实现供h5调用的native接口

在app开发中使用webview,经常需要从js端调用和原生相关的交互功能。那么这样一层bridge的开发工作具体采用什么方案来实现呢?
JS call OC:
方案1:
最古老也是使用最广泛、且跨平台的方案是在页面内嵌入一个iframe,然后通过该iframe触发的webview相关事件来进行hook,从而达到通信的目的。
其中回调方法的传递是通过生成一个id并保存,来回传递id,在js端再通过id获取到对应的fuction实现回调。大名鼎鼎的cordova就是采用了这种方案实现了bridge。

方案2:
iOS7 苹果引入了javascriptcore引擎;而该引擎可以用作js 和原生代码交互的桥梁。 那具体到webview里面是怎样实现的呢?
javascriptcore的使用,离不开的是jscontext。
对于UIWebview,我们可以在webview的代理方法(比如webViewDidFinishLoad)中使用如下代码获取到jscontext并保存:

    // Undocumented access to UIWebView's JSContext
    // TODO: base64 of documentView.webView.mainFrame.javaScriptContext
    self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

但是对于wkwebview,因为其内部实现的原因,我们无法获取到jscontext,所以这里我们不展开(在文章结尾处我们会大概说一下wkwebview可以采用的方案)。

UIwebview下实现供h5调用的native接口有两种方式:
1. block
在webViewDidFinishLoad末尾插入如下代码(扫码示例):

    @weakify_self;
    self.context[@"scanQRCode"] = ^(JSValue *cb)
    {
        @strongify_self;
        self.scanQRCB = cb;

        OrderCapture *capture = [[OrderCapture alloc] init];
        capture.scanType = OrderCaptureScanTypeAll;
        capture.targetDelegate = self;
        [capture showDecodeView];
    };

这里的cb是js传递过来的回调函数,通过scanQRCB这个属性保存了起来,后面在扫码的delegate方法中可以通过它来调用回调函数:

//条形码返回结果
- (void)didFinishReader:(NSString *)value
{
    [self.scanQRCB callWithArguments:@[value]];
}

js调用的形式(注意:在window上直接调用):

<button onclick = "window.scanQRCode(callback)">点击我弹出原生的扫码!</button>
  1. 通过JSExport协议包装方法
    首先我们要为这些方法注册一个共同的命名空间了(这里叫wq):
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    // Undocumented access to UIWebView's JSContext
    // TODO: base64 of documentView.webView.mainFrame.javaScriptContext
    self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

    // do the bridge below... here we use jsexport to do the bridge.

    self.context[@"wq"] = self;
}

实现原生的方法:

- (void)nativeAlert:(NSString *)title cb:(JSValue *)value
{
    self.alertCB = value;

    @weakify_self;
    dispatch_async(dispatch_get_main_queue(), ^{
        @strongify_self;
        self.alert = [[UIAlertView alloc] initWithTitle:title message:@""
                                               delegate:self
                                      cancelButtonTitle:@"取消"
                                      otherButtonTitles:nil, nil];
        [self.alert show];
    });

}

这里的参数应该是和js调用时的顺序对应,jsvalue可以对应js的function。

下面就是实现jsexport协议了,可以放在你的webview容器vc的.h最上面。

@protocol WqJSExport <JSExport>

JSExportAs
(openUrl,
 - (void)openUrlWithUrl:(NSString *)url title:(NSString *)title
 );

//- (void)nativeAlert:(NSString *)title;

JSExportAs
(nativeAlert,
 - (void)nativeAlert:(NSString *)title cb:(JSValue *)value
 );

@end

这里实现了3个方法,分别演示了多参数、单参数、带回调的export实现。
因js只支持单个参数,因此需要使用JSExportAs来对多参数的情况进行包装。
如果只有一个参数,不需要用jsexportAs来包装。
3个方法的调用示例:

<button onclick = "window.wq.openUrl(url, title)">通过原生打开页面!</button>

<button onclick = "window.wq.nativeAlert(biaoti, alertCallback)">点击我弹出原生的alert!</button>

<button onclick = "window.wq.nativeAlert(biaoti, alertCallback)">点击我弹出原生的alert!</button>

那么这样jscore在uiwebview上提供给js的bridge实现就讲完了,这种方法的好处是实现非常清晰,且没有额外的iframe开销;不失为一种优雅的bridge解决方案。

而对于wkwebview来说,需要采用另外的方式来实现(window.webkit.messageHandlers.xxxMethod.postMessage),和上面的方法完全不同,就不再展开了。

而如果使用iframe的方案,可以同时在wkwebview和uiwebview上起作用,考虑同时支持两种webview的情况下使用这种方案是比较合理的,无需做很多额外的处理;关于这套方案的具体实现,有时间再来细说一下(其实不复杂)。

补充:
对于wkwebview,不会自动弹出alert、prompt还有另外一个什么来着,而是可以通过代理方法,需要处理好相应的代理方法才可以完成交互(别以为是bug了,哈哈)。
例(对于alert):

webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:
时间: 2024-09-12 11:26:09

使用javascriptcore实现供h5调用的native接口的相关文章

微信支付H5调用支付详解(java版)_java

最近项目需要微信支付,然后看了下微信公众号支付,,虽然不难,但是细节还是需要注意的,用了大半天时间写了个demo,并且完整的测试了一下支付流程,下面分享一下微信公众号支付的经验. 一.配置公众号微信支付  需要我们配置微信公众号支付地址和测试白名单. 比如:支付JS页面的地址为 http://www.xxx.com/shop/pay/ 那此处配置www.xxx.com/shop/pay/ 二.开发流程 借用微信公众号支付api(地址 http://pay.weixin.qq.com/wiki/d

H5、React Native、Native应用对比分析

每日更新关注:http://weibo.com/hanjunqiang  新浪微博!iOS开发者交流QQ群: 446310206 "存在即合理".凡是存在的,都是合乎规律的.任何新事物的产生总要的它的道理:任何新事物的发展总是有着取代旧事物的能力.React Native来的正是时候,一则是因为H5发展到一定程度的受限:二则是移动市场的迅速崛起强调团队快速响应和迭代:三则是用户的体验被放大,用户要求极致的快感,除非你牛x(例如:12306最近修改手机号需要用户自己发短信接收验证码).

开发流程- 公司要与其他网站合作,要求开发api接口,供合作方调用,返回的数据是json或xml格式的

问题描述 公司要与其他网站合作,要求开发api接口,供合作方调用,返回的数据是json或xml格式的 请问这个开发流程大概是怎样的?求解..................................................... 解决方案 asp.net webapi非常适合做这个,你只要按照你原来的方式编写函数调用,它会自动将提交的数据转换成对象,作为参数传入,以及传出的参数转换成json返回. 解决方案二: 不管你后台用.net还是java还是php,你只需要公布一个可以访问的

支付宝H5调用APP 系统繁忙,请稍后再试

今天真是RI了狗了,以前应该遇到过,还提交了工单咨询过.但是最近由于测试上线又走了一遍流程,由于支付项目部署在另一个环境,导致pid没有修改为正式环境的. 一直报错H5调用APP 系统繁忙,请稍后再试.我擦,就不能给个提示.

php-微信PHP服务器端调用图灵机器人接口失败

问题描述 微信PHP服务器端调用图灵机器人接口失败 采用官方提供的demo但是没有成功,求大神指点啊! protected function talk($content){ $key = 'xxxxxxx'; //这里填写你的apikey $re = json_decode(file_get_contents('http://www.tuling123.com/openapi/api?key='.$key.'&info='.$content),true); $code = $re['code']

stringbuilder-C#中调用C++Dll接口,字符串编码问题

问题描述 C#中调用C++Dll接口,字符串编码问题 在C#中调用C++Dll接口,接口返回一个多字节字符串,然后再C#中再次转码为宽字节字符串. 字符串内容都为汉字. **_问题: 如果汉字为偶数个,则一切正常: 如果汉字为奇数个,则C#获得的多字节字符串内容的最后一个字节被篡改成'?'的ascll码.(多字节编码时,每个汉字占三个字节) 本人没多少币,全部家当拿出来了,求大神指点.**_ C++代码: // 宽字节转多字节 __declspec(dllexport) void TCharTo

.net 调用百度统计接口 Data API

问题描述 .net 调用百度统计接口 Data API 网上只有php的示例,我在第一步调用登录时,返回的是0f 接口说明里面 也没有说明 这代表的是什么. 无从下手了.. 解决方案 你是指这个? http://tongji.baidu.com/open/api/ 这不是有API文档么 解决方案二: 发送httprequest,然后对应读取结果 解决方案三: 楼主问题解决了吗?请问是怎么解决的,我现在也是返回0f

C# 可以直接调用金税组件接口吗?

问题描述 C# 可以直接调用金税组件接口吗? C# 可以直接调用金税组件接口吗?还是需要通过别的程序来调用? 解决方案 看看这个实例有没有用:http://download.csdn.net/detail/wang781109/5692821 解决方案二: 谢了,可以调用,金税2.0平台是.net framework4.0开发的,跟我们平台不一致,引起错误.

方法-cxf客户端调用 不注册接口 怎么调用服务器端?

问题描述 cxf客户端调用 不注册接口 怎么调用服务器端? 请问一下 用cxf客户端只用url和输入参数即简单传一个String类型的参数,不用服务器端的接口和实现类 怎么调用服务器端的方法? 解决方案 http://blog.csdn.net/zy609398738/article/details/7484331 解决方案二: http://zhengjj-2009.iteye.com/blog/625675