JSCoreBridge 是基于 iOS 平台的 Apache Cordova 开源框架修改。Cordova 通过插件的方式作为桥梁实现了 Web 与 Native 之间的通信,而 JSCoreBridge 则在其基础上进行了精简修改(移除平时不常用的类和方法),改写了传统的通信机制。在保留 Cordova 实用的功能前提下,JSCoreBridge 优化了框架的大小并且减少了复杂的工程设置选项,还有效地提高了通信效率。JSCoreBridge 开源框架致力于为开发者提供更便捷的 Hybrid 开发体验。
JSCoreBridge 是在 Cordova 基础上修改的,它兼容大部分 Cordova 的用法,熟悉 Cordova 的开发者可以轻松上手。
gap://ready
”,并响应链接跳转事件;webView:shouldStartLoadWithRequest:navigationType:
截获该 gap 跳转;stringByEvaluatingJavaScriptFromString:
方法执行 Cordova JS 方法 nativeFetchMessages
获取 Web 当前的命令参数并转化为 CDVInvokedUrlCommand
对象;className
和 methodName
属性找到对应插件和对应的插件方法,并执行插件方法;stringByEvaluatingJavaScriptFromString:
方法执行 Cordova JS 方法 nativeCallback
,通过 CDVInvokedUrlCommand
的 callbackId
作为标识将结果发送给 Web 对应的回调。不再使用传统的 scheme 链接跳转截取和 stringByEvaluatingJavaScriptFromString:
执行 JS 的方法,通过 iOS7 新增的 JavaScriptCore.framework
来实现 JS 和 Native 之间的通信。
jsCoreBridge.js
的 exec
或者 execSync
方法直接将命令参数传给客户端;JSCInvokedPluginCommand
对象;className
和 methodName
属性找到对应插件和对应的插件方法,并执行插件方法;jsCoreBridge.js
的 nativeCallback
方法,通过 JSCInvokedPluginCommand
的 callbackId
作为标识将结果发送给 Web 对应的回调。
- 如果你想使用完整版的 JSCoreBridge,请将以下命令行添加到 Podfile 中:
pod 'JSCoreBridge'
- 如果你想使用精简版的 JSCoreBridge,请将以下命令行添加到 Podfile 中:
pod 'JSCoreBridge/JSCoreBridgeLite'
- 如果你想使用更兼容 Cordova 用法的 JSCoreBridge,请将以下命令行添加到 Podfile 中:
pod 'JSCCordova'
或者
pod 'JSCCordova/JSCCordovaLite'
注:Lite版的JSCoreBridge将不使用config.xml
进行功能选项配置,JSCoreBridgeLite只实现了最基本的通信;如果你准备使用JSCCordova,更多详细说明请参考Cordova用法兼容性。
JSCoreBridge框架可以通过CocoaPods Pod集成到工程,也可以手动下载源码添加,添加JSCoreBridge后,只需简单配置config.xml和jsCoreBridge.js即可使用,如果框架是手动添加的,则需要添加JavaScriptCore.framework
库。
config.xml
和jsCoreBridge.js
的相关说明将在下文中详细阐述。
JSCoreBridge Demo中有JSCoreBridge的详细使用样例代码,可下载参考。
jsCoreBridge.js
和你的网页目录在同一级,你也可以将jsCoreBridge.js
复制到该同级目录;jsCoreBridge.js
复制到沙盒中;jsCoreBridge.js
放到你的远程网站上;jsCoreBridge.js的使用原则是,保证你的html文件能够引用到。
jsCoreBridge.js对应于Cordova的cordova.js,通过jsCoreBridge
对象来调用,也兼容Cordova用法,可以通过cordova
对象调用,jsCoreBridge接口如下:
jsCoreBridge.version
获取当前JSCoreBridge Web平台JS版本号。
客户端JSCoreBridge框架对jsCoreBridge.js
有最低版本要求,通过CocoaPods Pod到工程的jsCoreBridge.js
相对于当前客户端JSCoreBridge框架都是最新的版本,可放心使用;如果你自行从其他途径下载jsCoreBridge.js
,请确保该版本能够兼容客户端JSCoreBridge框架。
jsCoreBridge.exec
执行客户端对应插件方法。
通过该方法,可以告诉客户端JSCoreBridge框架通过对应插件的对应方法去执行相应的事情,代码示例如下:
var params = {title: 'JSCoreBridge Demo'};
jsCoreBridge.exec(function (res) {
// 成功回调
}, function (err) {
// 失败回调
}, 'JSCTestPlugin', 'changeNavTitle', [params]);
- 第一个函数为成功回调,第二个函数为失败回调,通过res和err获取结果数据;当然,如果你不想收到回调,这两个参数可以传递空,如果你不希望接收res和err结果数据,回调函数你也可以不带参数;
JSCTestPlugin
为客户端对应的插件Plugin类名;changeNavTitle
为JSCTestPlugin
插件中对应的插件方法;- 最后一个参数则为Web传给客户端的参数,通过数组的方式传递,至于数组里面传递什么样的数据,由开发者自行决定;当然该参数你也可以传递空或者不传递。
jsCoreBridge.execSync
同步执行客户端对应插件方法。
与exec接口不同的是,该方法为同步操作,没有成功与失败回调函数,其他参数与exec
用法一致,其代码示例如下:
var version = jsCoreBridge.execSync('JSCTestPlugin', 'getAppVersionSync', null);
deviceready
JSCoreBridge运行环境已准备就绪监听事件。
以下示例代码可以用来监听JSCoreBridge准备完成:
document.addEventListener('deviceready', onDeviceReady, false)
为了保证客户端插件方法能够正确执行,请在deviceready
回调中或者回调执行后调用jsCoreBridge
对象的方法;
如果你在deviceready
回调中调用jsCoreBridge.exec
,不要期望客户端对应插件方法会在jsCoreBridgeDidReady:
之前调用,jsCoreBridge.exec
为异步操作,除非你使用jsCoreBridge.execSync
同步方法。
pause
客户端已经进入后台监听事件。
以下示例代码可以用来监听客户端已经进入后台:
document.addEventListener('pause', onPause, false)
resume
客户端即将进入前台监听事件。
以下示例代码可以用来监听客户端即将进入前台:
document.addEventListener('resume', onResume, false)
在Cordova中,config.xml是框架功能选项的配置文件,包含工程的一些信息,插件白名单,Web URL白名单,WebView属性设置等。同样的,在JSCoreBridge中,我们也将config.xml
移植了过来,并对一些配置选项进行了删减,以便达到一个轻量级的JSCoreBridge框架。
config.xml文件并不是必须的,当你使用JSCoreBridgeLite
时,将不再使用config.xml
文件来配置框架;当然,你也可以通过设置JSCWebViewController类的configEnabled
属性来关闭使用config.xml
,以使用一个最轻量化的JSCoreBridge。
JSCoreBridge在未使用config.xml
的状况下,其仅仅满足Web与Native之间通信的基本功能,不进行插件白名单验证,不进行Web URL白名单验证,并且WebView的相关属性都保持为系统默认状态。
想要了解如何配置config.xml
文件,可以进一步点击这里,进入Cordova官方网站进行了解。
当然,对于一般的开发者来说,JSCoreBridge中的config.xml样例已足够满足需求,你只需配置插件白名单即可,配置示例如下:
<feature name="JSCTestBasePlugin">
<param name="ios-package" value="JSCTestBasePlugin" />
<param name="onload" value="true" />
</feature>
一般来说,保持
feature
中的name
的值和param
中的value
值一致,当然你也可以不一致,但必须保证param
中的value
值和对应的插件类名一致;如果希望插件在JSCoreBridge初始化时就加载,可以通过<param name="onload" value="true" />
来设置,如果不需要,可以省略该行。
在JSCoreBridge中,以下配置选项目前尚未实现:
BackupWebStorage
, TopActivityIndicator
, ErrorUrl
, OverrideUserAgent
, AppendUserAgent
, target-device
, deployment-target
, CordovaWebViewEngine
, SuppressesLongPressGesture
, Suppresses3DTouchGesture
)在JSCoreBridge中,以下配置选项不再需要添加:
id
, version
,defaultlocale
,ios-CFBundleVersion
,xmlns
,xmlns:cdv
)config.xml
,请在JSCoreBridge/Optional
目录下将config.xml
复制到其他目录并添加到工程中使用;
JSCWebViewController是JSCoreBridge框架直接提供给开发者使用的ViewController,可以直接使用,也可以根据需求进行继承使用,其部分API说明如下:
bridgeDelegate
JSCoreBridge代理。可以通过这个对象执行相应的代理方法,具体请参考JSCBridgeDelegate。
configFilePath
config.xml文件路径。默认为
nil
,从 Bundle 根目录获取,如果设置了该属性,则从该属性路径获取,不支持网络地址。
configEnabled
是否启用config配置功能。默认启用,如需关闭,可设置为NO;当使用JSCoreBridgeLite时,
configEnabled
属性不可用,始终为关闭状态。
shouldAutoLoadURL
是否自动加载URL。默认自动加载通过
initWithUrl:
初始化的URL,设置为NO将关闭自动加载。
- (instancetype)initWithUrl:(NSString *)url
通过字符串连接初始化URL。可以在
JSCWebViewController
子类中重写该方法。
- (void)loadURL:(NSURL *)URL
- (void)loadHTMLString:(NSString *)htmlString
通过调用以上两个方法进行网页的手动加载。
- (void)jsCoreBridgeWillReady:(UIWebView *)webView
- (void)jsCoreBridgeDidReady:(UIWebView *)webView
JSCoreBridge将要准备就绪和已准备就绪的回调。分别在
deviceready
通知回调执行之前和之后调用,方便开发者在这两个时刻进行相应的操作,可以在JSCWebViewController
子类中重写该方法使用。
JSCBridgeDelegate是JSCoreBridge
类的代理,可以通过这个代理向Web发送结果数据,执行JS等。该代理作为JSCWebViewController
和JSCPlugin
的属性来使用。
- (void)registerPlugin:(JSCPlugin *)plugin withPluginName:(NSString *)pluginName
将已存在的插件通过类名注册到插件白名单中。如果使用
config.xml
,那么JSCoreBridge将只会识别config.xml
配置好的插件白名单,不在白名单范围内的插件将不会被加载和使用,可以通过该方法将插件注册到白名单中。
- (nullable __kindof JSCPlugin *)getPluginInstance:(NSString *)pluginName
通过插件类名来获取插件对象。可以通过该方法获取对应Plugin的实例对象。
- (void)sendPluginResult:(JSCPluginResult *)result forCallbackId:(NSString *)callbackId
向Web发送结果数据。将结果数据以JSCPluginResult对象实例进行封装,并以
callbackId
作为回调标识发送给Web。代码示例如下:
NSDictionary *message = @{@"resCode":@"0", @"resMsg":@"OK"};
// 将要返回给Web的结果以字典形式通过JSCPluginResult初始化
JSCPluginResult *result = [JSCPluginResult resultWithStatus:JSCCommandStatus_OK messageAsDictionary:message];
// 发送结果
[self.bridgeDelegate sendPluginResult:result forCallbackId:command.callbackId];
- (JSValue *)evaluateScript:(NSString *)script
- (JSValue *)callScriptFunction:(NSString *)funcName withArguments:(nullable NSArray *)arguments
执行JS和调用JS函数方法。方法一实际通过
JSContext
的evaluateScript:
方法执行JS;方法二通过JSContext
的callWithArguments:
调用JS函数,需要传递函数名称funcName
,如果该函数直属于Window对象,则直接传递函数名,如果该函数并不直属于Window对象,则可以通过键值路径的方式调用,代码示例如下:
[self.bridgeDelegate callScriptFunction:@"jsCoreBridge.fireDocumentEvent" withArguments:@[@"deviceready"]];
其中
jsCoreBridge
必须为Window的属性。
- (void)onMainThreadEvaluateScript:(NSString *)script
- (void)onMainThreadCallScriptFunction:(NSString *)funcName withArguments:(nullable NSArray *)arguments
与上述两个方法作用一致,只是这两个方法确保执行JS和调用JS函数在主线程上。在特定情况下不在主线程上执行JS可能导致程序崩溃,通过这两个方法可以解决这个问题。
- (void)runInBackground:(void (^)(void))block
- (void)runOnMainThread:(void (^)(void))block
辅助类方法,方便开发者在后台或者主线程上处理对应的事情。
JSCPlugin就是我们所说的插件,这是一个基类,开发者需要根据需求来分类建立多个插件,而这些插件都应当继承于JSCPlugin来使用。
JSCPlugin插件方法的声明示例如下:
- (void)changeNavTitle:(JSCInvokedPluginCommand *)command;
- (void)sendEmail:(JSCInvokedPluginCommand *)command;
- (NSString *)getAppVersionSync:(JSCInvokedPluginCommand *)command; // 同步操作
接收一个JSCInvokedPluginCommand对象,JSCoreBridge支持同步操作,如果需要使用同步操作,需要对应有一个返回值,而该返回值必须是一个Object对象,否则将给Web返回空的结果数据。
JSCPlugin的部分API说明如下:
webView
webViewController
获取当前
webView
和webViewController
。
backupCallbackId
用于备份某个插件方法的
callbackId
。当你在某个插件方法中使用了某个对象,而该对象的一些操作需要在代理方法中获取结果时,可以通过该属性来保存当前插件方法的回调callbackId
,以便在代理回调中继续使用该callbackId
来发送结果数据。具体用法可参考JSCoreBridge Demo。
当然,该用法只适用于当前插件只有一个插件方法需要使用backupCallbackId,如果多个插件方法需要保存callbackId,建议参考cordova官方插件的一些写法,将callbackId
作为其对应使用对象的属性成员,如CDVCamera插件,CDVCameraPicker
继承UIImagePickerController
,并拥有callbackId
属性。
- (void)pluginDidInitialize
插件初始化后回调该方法,可以在该方法中进行一些初始化的操作,类似于
UIViewController
的viewDidLoad
方法。
- (void)canCallPlugin
JSCoreBridge调用插件时,会先通过该方法进行权限验证,如果返回YES,则可以正常调用插件,如果返回NO,则无法调用。开发者可以通过该方法进行一些权限的条件设置。
JSCoreBridge给Web发送的结果数据通过JSCPluginResult对象进行封装,以字符串、数组、Cordova特定的格式等多种数据格式进行发送。
status
结果状态,当
JSCCommandStatus_OK
时将结果发送给成功回调,当JSCCommandStatus_ERROR
时将结果发送给失败回调。
keepCallback
是否需要继续回调。默认为NO,同一个
callbackId
只能发送一次结果数据,设为YES,则支持多次回调。例如写一个监听客户端某个按钮点击事件的插件方法,用户点击按钮一次,给Web发送一次结果消息,这种使用场景就需要将keepCallback
设置为YES才能保证多次回调。
JSCoreBridge通过JSCInvokedPluginCommand对象将Web发送给Native的命令参数进行封装,其属性包括如下成员:
callbackId
className
methodName
arguments
分别为回调的callbackId
标识、插件类名、插件方法名、Web传给客户端的参数,JSCoreBridge正是通过这些属性来完成Web交给Native的任务。
对于框架的其他类,默认为私有状态,建议开发者不要随意调用或修改,在使用框架的过程中如遇任何问题和bug欢迎联系本人沟通商讨解决。
JSCoreBridge在以下三种情况下默认会以键resCode
和resMsg
给Web返回对应的code码和错误信息:
@{@"resCode": @"4001", @"resMsg": @"ERROR: Plugin 'PluginName' not found, or is not a JSCPlugin. Check your plugin mapping in config.xml."}
出现这种情况的原因可能是Web传错了插件名,或者客户端没有对应的插件,或者使用了
config.xml
但白名单中没有添加该插件。
@{@"resCode": @"4002", @"resMsg": @"ERROR: Plugin 'PluginName' can not be called, it is not allowed."}
这种情况出现的原因在于插件方法
canCallPlugin
的返回值为NO。
@{@"resCode": @"4003", @"resMsg": @"ERROR: Method 'MethodName' not defined in Plugin 'PluginName'."}
出现这种情况的原因可能是Web传错了插件方法名,或者客户端没有对应的插件方法。
开发者可以通过定义以下宏来自定义JSCoreBridge给Web返回的错误信息的键和值,代码示例:
#define JSC_KEY_RESCODE @"errCode"
#define JSC_KEY_RESMSG @"errMsg"
#define JSC_RESCODE_PLUGIN_NOT_FOUND @"401"
#define JSC_RESCODE_PLUGIN_CANNOT_CALL @"402"
#define JSC_RESCODE_METHOD_NOT_DEFINED @"403"
#define JSC_RESMSG_PLUGIN_NOT_FOUND @"ERROR: Plugin not found, or is not a JSCPlugin. Check your plugin mapping in config.xml."
#define JSC_RESMSG_PLUGIN_CANNOT_CALL @"ERROR: Plugin can not be called, it is not allowed."
#define JSC_RESMSG_METHOD_NOT_DEFINED @"ERROR: Method not defined in Plugin."
在返回成功和失败结果数据时,建议开发者通过code和message的形式给Web返回结果信息,以便Web开发者能够通过code和message识别当前情况或问题所在。
关于JSCoreBridge加载网页时,Web和Native对应回调方法的执行顺序,这里需要特别说明:
jsCoreBridge.js
在html页面直接引用,如下所示: <script type="text/javascript" src="jscorebridge.js"></script>
各个回调的执行顺序如下:
window.onload
如果你在Web中写了该方法,将在此刻执行。
load
Web监听
Window
load
事件的回调,如window.addEventListener("load", jscWindowOnLoad, false)
。如果你在Web中写了该方法,将在此刻执行。
jsCoreBridgeWebViewDidFinishLoad
JSCWebViewController类中的回调方法,实际上是
WebView
的webViewDidFinishLoad:
代理方法。
jsCoreBridgeWillReady
JSCWebViewController类中JSCoreBridge即将准备就绪时的回调
deviceready
Web监听JSCoreBridge已准备就绪的通知回调
jsCoreBridgeDidReady
JSCWebViewController类中JSCoreBridge准备就绪之后的回调
jsCoreBridge.js
是通过别的JS通过appendChild的方式加入,如下所示: var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'jscorebridge/jscorebridge.js';
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
各个回调的执行顺序如下:
jsCoreBridgeWebViewDidFinishLoad
window.onload
jscWindowOnLoad
(load
)jsCoreBridgeWillReady
deviceready
jsCoreBridgeDidReady
与第一种情况不同的是,当
jsCoreBridge.js
通过代码的方式加入后,WebView
不会等待jsCoreBridge.js
加载完再回调webViewDidFinishLoad:
。
通过测试可以发现,所有JS在window.onload
或者load
监听事件回调中就已经加载完毕。如果是第二种执行顺序,实现上JSCoreBridge选择在load
监听事件回调里发送deviceready
通知。不选择window.onload
的原因在于,如果客户端向JSContext
中添加window.onload
方法,那么对于Web开发人员来说,如果他再写了window.onload
方法,该方法将不再调用。
对于第一种执行顺序,JSCoreBridge是直接在webViewDidFinishLoad:
代理方法中发送deviceready
通知,因为此时load
监听事件回调已完成,在webViewDidFinishLoad:
中可以直接获取到jsCoreBridge.js
的jsCoreBridge
对象。
开发者可参考以上两种情况的执行顺序来决定自己在开发中如何在各个回调中处理相应的事情。
JSCoreBridge基于Cordova修改,无论是Web平台还是Native平台,都保留了其原始的使用方法:
cordova.exec(successFuntion, failFuntion, 'pluginName', 'methodName', [params])
方式调用,同时新增jsCoreBridge对象调用的方式,新增jsCoreBridge.execSync
同步方法。config.xml
配置方式与Cordova一致,只是删减了部分配置选项。Plugin插件方法的编写也保持与Cordova一致。新增同步插件方法,唯一的区别在于各个相关联的类名都对应修改成JSCoreBridge框架的类,并在实现上可能稍作修改。JSCoreBridge
Pod库中,需单独Pod JSCCordova
库。JSCCordova的目的是最大化地去兼容Cordova的用法。如果开发者已经在使用Cordova,想将Cordova替换为JSCoreBridge而不想修改太多源代码,JSCCordova此时就发挥了其很大的作用。当然,需要说明的一点是,使用JSCCordova并不能保证你的代码完全不用修改。由于JSCoreBridge在实现上的差异,开发者在使用Cordova API的差异,以及本身JSCCordova也没有完全兼容Cordova所有接口等原因,开发者在使用JSCCordova时,可能会出现相应的报错,但这些报错是可以通过解决来避免的。JSCoreBridge框架通过KVC的方式[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]
,从webView中获取JSContext
,有涉嫌使用苹果私有API的风险,尽管该方法在网上被大量应用而未遭到苹果拒绝,但本人无法保证能够100%通过审核,如果您对这些问题有所顾虑,请评估风险后再使用。
JSCoreBridge会持续跟进和更新,之后有更好的实现方法,会第一时间来屏蔽这个风险。
尽管本框架已经进行过多次自测,但还未进行大范围的测试,可能会存在未知的bug,如果在使用本框架时,因为未知bug导致的风险需要您自行承担。
欢迎使用本框架的各方反馈在使用中遇到的各种问题和bug。
JSCoreBridge框架是基于我深入了解Apache Cordova后在其基础上修改和封装的,本着开源的思想,现上传至GitHub,并提供CocoaPods支持,后续会持续跟进更新。如果你在使用本框架,欢迎及时反馈你在使用过程中遇到的各种问题和bug,也欢迎大家与我沟通和分享更多互联网技术。iPhuan其他开源资源将会不定期的更新至iPhuanLib。
邮箱:[email protected]
QQ:519310392
添加QQ时请备注JSCoreBridge
更新日期:2017年2月18日
更新说明:
- 发布JSCoreBridge第一个版本。
更新日期:2017年2月19日
更新说明:
- 新增JSCCordova,JSCCordova有助于JSCoreBridge更好地兼容Cordova用法。
文档相应介绍:点击查看