本文只对JS与Native之间的交互进行源码阅读。至于Cordova如何开发插件等等,请参考Cordova官方文档:https://cordova.apache.org/docs/en/latest/
Native回调JS
流程图
解析
Native
Native方法执行完,通过sendPluginResult开始,回调结果给jssendPluginResult:
1
2
3
4
5
6
7
8
9
10
11- (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:
方法执行JS1
2
3
4
5
6
7
8
9
10
11
12
13
14
15- (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
1
2
3
4
5
6
7
8
9
10iOSExec.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返回的结果进行回调处理1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29/**
* 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是作为一个功能整合的类,这里面包含的命令队列、协议代理,在上面都已经有涉及
- 内部主要初始化插件、配置、设置代理、处理生命周期
1
2
3
4
5
6
7
8
9@interface CDVViewController : UIViewController <CDVScreenOrientationDelegate>{
@protected
id <CDVWebViewEngineProtocol> _webViewEngine;
@protected
id <CDVCommandDelegate> _commandDelegate;
@protected
CDVCommandQueue* _commandQueue;
NSString* _userAgent;
}
CDVUIWebViewEngine:CDVPlugin 以插件的形式实现
- 成员变量
engineWebView
,创建webview,赋值engineWebView
- pluginInitialize,初始化插件,设置代理。
CDVUIWebViewNavigationDelegate
代理在此处设置
- 成员变量