本文只对JS与Native之间的交互进行源码阅读。至于Cordova如何开发插件等等,请参考Cordova官方文档:https://cordova.apache.org/docs/en/latest/
Native回调JS
流程图
解析
Native
Native方法执行完,通过sendPluginResult开始,回调结果给jssendPluginResult:
- (void)sendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId { int status = [result.status intValue]; BOOL keepCallback = [result.keepCallback boolValue]; NSString* argumentsAsJSON = [result argumentsAsJSON]; BOOL debug = NO; NSString* js = [NSString stringWithFormat:@"cordova.require('cordova/exec').nativeCallback('%@',%d,%@,%d, %d)", callbackId, status, argumentsAsJSON, keepCallback, debug]; [self evalJsHelper:js]; }
evalJsHelper:
执行JS,这里的流程跟JS调Native执行JS一模一样的代码,利用UIWebView-stringByEvaluatingJavaScriptFromString:
方法执行JS- (void)evalJsHelper2:(NSString*)js { [_viewController.webViewEngine evaluateJavaScript:js completionHandler:^(id obj, NSError* error) { // TODO: obj can be something other than string if ([obj isKindOfClass:[NSString class]]) { NSString* commandsJSON = (NSString*)obj; if ([commandsJSON length] > 0) { CDV_EXEC_LOG(@"Exec: Retrieved new exec messages by chaining."); } [_commandQueue enqueueCommandBatch:commandsJSON]; [_commandQueue executePending]; } }]; }
- 其执行回调里仍然有处理命令队列的代码
JS
nativeCallback
这一步,并不是直接就回调结束了,还会通过nativeEvalAndFetch-nativeFetchMessages
返回当前的commandQueue里的内容,会到前一步evaluateJavaScript
执行结束的回调中,直到全部执行结束。触发cordova.callbackFromNative
iOSExec.nativeCallback = function(callbackId, status, message, keepCallback, debug) { return iOSExec.nativeEvalAndFetch(function() { var success = status === 0 || status === 1; var args = convertMessageToArgsNativeToJs(message); function nc2() { cordova.callbackFromNative(callbackId, success, status, args, keepCallback); } setTimeout(nc2, 0); }); };
callbackFromNative
这里的源码注释也非常清楚了,根据Native返回的结果进行回调处理/** * Called by native code when returning the result from an action. */ callbackFromNative: function(callbackId, isSuccess, status, args, keepCallback) { try { var callback = cordova.callbacks[callbackId]; if (callback) { if (isSuccess && status == cordova.callbackStatus.OK) { callback.success && callback.success.apply(null, args); } else if (!isSuccess) { callback.fail && callback.fail.apply(null, args); } /* else Note, this case is intentionally not caught. this can happen if isSuccess is true, but callbackStatus is NO_RESULT which is used to remove a callback from the list without calling the callbacks typically keepCallback is false in this case */ // Clear callback if not expecting any more results if (!keepCallback) { delete cordova.callbacks[callbackId]; } } } catch (err) { // ... } }
其他补充
CDVViewController
- 看构成,就知道CDVViewController是作为一个功能整合的类,这里面包含的命令队列、协议代理,在上面都已经有涉及
- 内部主要初始化插件、配置、设置代理、处理生命周期
@interface CDVViewController : UIViewController <CDVScreenOrientationDelegate>{ @protected id <CDVWebViewEngineProtocol> _webViewEngine; @protected id <CDVCommandDelegate> _commandDelegate; @protected CDVCommandQueue* _commandQueue; NSString* _userAgent; }
CDVUIWebViewEngine:CDVPlugin 以插件的形式实现
- 成员变量
engineWebView
,创建webview,赋值engineWebView
- pluginInitialize,初始化插件,设置代理。
CDVUIWebViewNavigationDelegate
代理在此处设置
- 成员变量