cordova封裝了一套js和OC通訊的代碼,cordova.js下的iOSExex是關鍵的js去調原生的發起點。android
function iOSExec() { var successCallback, failCallback, service, action, actionArgs; var callbackId = null; if (typeof arguments[0] !== 'string') { // FORMAT ONE successCallback = arguments[0]; failCallback = arguments[1]; service = arguments[2]; action = arguments[3]; actionArgs = arguments[4]; // Since we need to maintain backwards compatibility, we have to pass // an invalid callbackId even if no callback was provided since plugins // will be expecting it. The Cordova.exec() implementation allocates // an invalid callbackId and passes it even if no callbacks were given. callbackId = 'INVALID'; } else { throw new Error('The old format of this exec call has been removed (deprecated since 2.1). Change to: ' + 'cordova.exec(null, null, \'Service\', \'action\', [ arg1, arg2 ]);' ); } // If actionArgs is not provided, default to an empty array actionArgs = actionArgs || []; // Register the callbacks and add the callbackId to the positional // arguments if given. if (successCallback || failCallback) { callbackId = service + cordova.callbackId++; cordova.callbacks[callbackId] = {success:successCallback, fail:failCallback}; } actionArgs = massageArgsJsToNative(actionArgs); var command = [callbackId, service, action, actionArgs]; // Stringify and queue the command. We stringify to command now to // effectively clone the command arguments in case they are mutated before // the command is executed. commandQueue.push(JSON.stringify(command)); // If we're in the context of a stringByEvaluatingJavaScriptFromString call, // then the queue will be flushed when it returns; no need for a poke. // Also, if there is already a command in the queue, then we've already // poked the native side, so there is no reason to do so again. if (!isInContextOfEvalJs && commandQueue.length == 1) { pokeNative(); } }
OC裏執行js很簡單,就是evaluateJavaScript:js方法,執行js的地方最後都走到evalJsHelper2這裏ios
- (void)evalJsHelper2:(NSString*)js { CDV_EXEC_LOG(@"Exec: evalling: %@", [js substringToIndex:MIN([js length], 160)]); [_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]; } }]; }
能夠看到就是evaluateJavaScript方法去執行一段js。web
下面具體來看一下:併發
1.首先一個插件確定在js裏去調用,其實都是走exec這個方法,具體是走iOSExec(),仍是androidExec(),對應各自平臺。async
iOSExec在上面已經粘貼了,最下面有個pokeNative(),看方法名也猜出來是去調native端的代碼ide
function pokeNative() { ... execIframe.contentWindow.location = 'gap://ready';
...
}
- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType { NSURL* url = [request URL]; CDVViewController* vc = (CDVViewController*)self.enginePlugin.viewController; /* * Execute any commands queued with cordova.exec() on the JS side. * The part of the URL after gap:// is irrelevant. */ if ([[url scheme] isEqualToString:@"gap"]) { [vc.commandQueue fetchCommandsFromJs]; // The delegate is called asynchronously in this case, so we don't have to use // flushCommandQueueWithDelayedJs (setTimeout(0)) as we do with hash changes. [vc.commandQueue executePending]; return NO; } /* * Give plugins the chance to handle the url */ BOOL anyPluginsResponded = NO; BOOL shouldAllowRequest = NO; for (NSString* pluginName in vc.pluginObjects) { CDVPlugin* plugin = [vc.pluginObjects objectForKey:pluginName]; SEL selector = NSSelectorFromString(@"shouldOverrideLoadWithRequest:navigationType:"); if ([plugin respondsToSelector:selector]) { anyPluginsResponded = YES; shouldAllowRequest = (((BOOL (*)(id, SEL, id, int))objc_msgSend)(plugin, selector, request, navigationType)); if (!shouldAllowRequest) { break; } } } if (anyPluginsResponded) { return shouldAllowRequest; } /* * Handle all other types of urls (tel:, sms:), and requests to load a url in the main webview. */ BOOL shouldAllowNavigation = [self defaultResourcePolicyForURL:url]; if (shouldAllowNavigation) { return YES; } else { [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]]; } return NO; }
到這就是js調用native的過程了。post
另外:fetch
// Enable this to log all exec() calls. #define CDV_ENABLE_EXEC_LOGGING 0
設置爲1,把全部執行exec的log打出來,就看到全部執行js的地方this