Cordova£¬¶ÔÕâ¸öÃû×Ö´ó¼Ò¿ÉÄܱȽÏİÉú£¬´ó¼Ò¿Ï¶¨Ìý¹ý
PhoneGap Õâ¸öÃû×Ö£¬Cordova ¾ÍÊÇ PhoneGap ±» Adobe ÊÕ¹ººóËù¸ÄµÄÃû×Ö¡£
Cordova ÊÇÒ»¸ö¿ÉÒÔÈà JS ÓëÔÉú´úÂë(°üÀ¨ Android µÄ java£¬iOS µÄ Objective-C
µÈ)»¥ÏàͨÐŵÄÒ»¸ö¿â£¬²¢ÇÒÌṩÁËһϵÁеIJå¼þÀ࣬±ÈÈç JS Ö±½Ó²Ù×÷±¾µØÊý¾Ý¿âµÄ²å¼þÀà¡£
ÕâЩ²å¼þÀà¶¼ÊÇ»ùÓÚ JS Óë Objective-C ¿ÉÒÔ»¥ÏàͨÐŵĻù´¡µÄ£¬ÕâÆªÎÄÕÂ˵˵ Cordova
ÊÇÈçºÎ×öµ½ JS Óë Objective-C »¥ÏàͨÐŵ쬽âÊÍÈçºÎ»¥ÏàͨÐÅÐèҪŪÇå³þÏÂÃæÈý¸öÎÊÌ⣺
1.JS Ôõô¸ú Objective-C ͨÐÅ
2.Objective-C Ôõô¸ú JS ͨÐÅ
3.JS ÇëÇó Objective-C£¬Objective-C ·µ»Ø½á¹û¸ø
JS£¬ÕâÒ»À´Ò»ÍùÊÇÔõô´®ÆðÀ´µÄ
Cordova ÏÖÔÚ×îа汾ÊÇ 2.7.0£¬±¾ÎÄÒ²ÊÇ»ùÓÚ 2.7.0 °æ±¾½øÐзÖÎöµÄ¡£
JS Ôõô¸ú Objective-C ͨÐÅ
JS Óë Objetive-C ͨÐŵĹؼü´úÂëÈçÏ£º(µã»÷´úÂë¿òÓÒÉϽǵÄÎļþÃûÁ´½Ó£¬¿ÉÖ±½ÓÌø×ª¸ÃÎļþÔÚ
github µÄµØÖ·)
function iOSExec() { ... if (!isInContextOfEvalJs && commandQueue.length == 1) { // Èç¹ûÖ§³Ö XMLHttpRequest£¬ÔòʹÓà XMLHttpRequest ·½Ê½ if (bridgeMode != jsToNativeModes.IFRAME_NAV) { // This prevents sending an XHR when there is already one being sent. // This should happen only in rare circumstances (refer to unit tests). if (execXhr && execXhr.readyState != 4) { execXhr = null; } // Re-using the XHR improves exec() performance by about 10%. execXhr = execXhr || new XMLHttpRequest(); // Changing this to a GET will make the XHR reach the URIProtocol on 4.2. // For some reason it still doesn't work though... // Add a timestamp to the query param to prevent caching. execXhr.open('HEAD', "/!gap_exec?" + (+new Date()), true); if (!vcHeaderValue) { vcHeaderValue = /.*\((.*)\)/.exec(navigator.userAgent)[1]; } execXhr.setRequestHeader('vc', vcHeaderValue); execXhr.setRequestHeader('rc', ++requestCount); if (shouldBundleCommandJson()) { // ÉèÖÃÇëÇóµÄÊý¾Ý execXhr.setRequestHeader('cmds', iOSExec.nativeFetchMessages()); } // ·¢ÆðÇëÇó execXhr.send(null); } else { // Èç¹û²»Ö§³Ö XMLHttpRequest£¬ÔòʹÓÃ͸Ã÷ iframe µÄ·½Ê½£¬ÉèÖà iframe µÄ src ÊôÐÔ execIframe = execIframe || createExecIframe(); execIframe.src = "gap://ready"; } } ... } |
JS ʹÓÃÁËÁ½ÖÖ·½Ê½À´Óë Objective-C ͨÐÅ£¬Ò»ÖÖÊÇʹÓà XMLHttpRequest ·¢ÆðÇëÇóµÄ·½Ê½£¬ÁíÒ»ÖÖÔòÊÇͨ¹ýÉèÖÃ͸Ã÷µÄ
iframe µÄ src ÊôÐÔ£¬ÏÂÃæÏêϸ½éÉÜÒ»ÏÂÁ½ÖÖ·½Ê½ÊÇÔõô¹¤×÷µÄ£º
XMLHttpRequest bridge
JS ¶ËʹÓà XMLHttpRequest ·¢ÆðÁËÒ»¸öÇëÇó£º
execXhr.open('HEAD', "/!gap_exec?" + (+new
Date()), true); £¬ÇëÇóµÄµØÖ·ÊÇ /!gap_exec£»²¢°ÑÇëÇóµÄÊý¾Ý·ÅÔÚÁËÇëÇóµÄ header
ÀïÃæ£¬¼ûÕâ¾ä´úÂ룺execXhr.setRequestHeader('cmds', iOSExec.nativeFetchMessages());
¡£
¶øÔÚ Objective-C ¶ËʹÓÃÒ»¸ö NSURLProtocol µÄ×ÓÀàÀ´¼ì²éÿ¸öÇëÇó£¬Èç¹ûµØÖ·ÊÇ
/!gap_exec µÄ»°£¬ÔòÈÏΪÊÇ Cordova ͨÐŵÄÇëÇó£¬Ö±½ÓÀ¹½Ø£¬À¹½Øºó¾Í¿ÉÒÔͨ¹ý·ÖÎöÇëÇóµÄÊý¾Ý£¬·Ö·¢µ½²»Í¬µÄ²å¼þÀà(CDVPlugin
ÀàµÄ×ÓÀà)µÄ·½·¨ÖУº
+ (BOOL)canInitWithRequest:(NSURLRequest*)theRequest { NSURL* theUrl = [theRequest URL]; NSString* theScheme = [theUrl scheme];
// ÅжÏÇëÇóÊÇ·ñΪ /!gap_exec
if ([[theUrl path] isEqualToString:@"/!gap_exec"])
{
NSString* viewControllerAddressStr = [theRequest
valueForHTTPHeaderField:@"vc"];
if (viewControllerAddressStr == nil) {
NSLog(@"!cordova request missing vc header");
return NO;
}
long long viewControllerAddress = [viewControllerAddressStr
longLongValue];
// Ensure that the UCCDVViewController has not
been dealloc'ed.
UCCDVViewController* viewController = nil;
@synchronized(gRegisteredControllers) {
if (![gRegisteredControllers containsObject:
[NSNumber numberWithLongLong:viewControllerAddress]])
{
return NO;
}
viewController = (UCCDVViewController*)(void*)viewControllerAddress;
}
// »ñÈ¡ÇëÇóµÄÊý¾Ý
NSString* queuedCommandsJSON = [theRequest valueForHTTPHeaderField:@"cmds"];
NSString* requestId = [theRequest valueForHTTPHeaderField:@"rc"];
if (requestId == nil) {
NSLog(@"!cordova request missing rc header");
return NO;
}
...
}
...
} |
Cordova ÖÐÓÅÏÈʹÓÃÕâÖÖ·½Ê½£¬Cordova.js ÖеÄ×¢ÊÍÓÐÌἰΪʲôÓÅÏÈʹÓà XMLHttpRequest
µÄ·½Ê½£¬¼°ÎªÊ²Ã´±£ÁôµÚ¶þÖÖ iframe bridge µÄͨÐÅ·½Ê½:
// XHR mode does not work on iOS 4.2, so default to IFRAME_NAV for such devices. // XHR mode¡¯s main advantage is working around a bug in -webkit-scroll, which // doesn¡¯t exist in 4.X devices anyways |
iframe bridge
ÔÚ JS ¶Ë´´½¨Ò»¸ö͸Ã÷µÄ iframe£¬ÉèÖÃÕâ¸ö ifame µÄ src Ϊ×Ô¶¨ÒåµÄÐÒ飬¶ø ifame
µÄ src ¸ü¸Äʱ£¬UIWebView »áÏȻص÷Æä delegate µÄ webView:shouldStartLoadWithRequest:navigationType:
·½·¨£¬¹Ø¼ü´úÂëÈçÏ£º
// UIWebView ¼ÓÔØ URL ǰ»Øµ÷µÄ·½·¨£¬·µ»Ø YES£¬Ôò¿ªÊ¼¼ÓÔØ´Ë URL£¬·µ»Ø NO£¬ÔòºöÂÔ´Ë URL - (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType { NSURL* url = [request URL];
/*
* Execute any commands queued with cordova.exec()
on the JS side.
* The part of the URL after gap:// is irrelevant.
*/
// ÅжÏÊÇ·ñ Cordova µÄÇëÇ󣬶ÔÓÚ JS ´úÂëÖÐ execIframe.src =
"gap://ready" Õâ¾ä
if ([[url scheme] isEqualToString:@"gap"])
{
// »ñÈ¡ÇëÇóµÄÊý¾Ý£¬²¢¶ÔÊý¾Ý½øÐзÖÎö¡¢´¦Àí
[_commandQueue fetchCommandsFromJs];
return NO;
}
...
} |
Objective-C Ôõô¸ú JS ͨÐÅ
ÊìϤ UIWebView Ó÷¨µÄͬѧ¶¼ÖªµÀ UIWebView ÓÐÒ»¸öÕâÑùµÄ·½·¨ stringByEvaluatingJavaScriptFromString:£¬Õâ¸ö·½·¨¿ÉÒÔÈÃÒ»¸ö
UIWebView ¶ÔÏóÖ´ÐÐÒ»¶Î JS ´úÂ룬ÕâÑù¾Í¿ÉÒÔ´ïµ½ Objective-C ¸ú JS ͨÐŵÄЧ¹û£¬ÔÚ
Cordova µÄ´úÂëÖжദÓõ½ÁËÕâ¸ö·½·¨£¬ÆäÖÐ×îÖØÒªµÄÁ½´¦ÈçÏ£º
»ñÈ¡ JS µÄÇëÇóÊý¾Ý
- (void)fetchCommandsFromJs { // Grab all the queued commands from the JS side. NSString* queuedCommandsJSON = [_viewController.webView stringByEvaluatingJavaScriptFromString: @"cordova.require('cordova/exec').nativeFetchMessages()"];
[self enqueCommandBatch:queuedCommandsJSON];
if ([queuedCommandsJSON length] > 0) {
CDV_EXEC_LOG(@"Exec: Retrieved new exec messages
by request.");
}
} |
°Ñ JS ÇëÇóµÄ½á¹û·µ»Ø¸ø JS ¶Ë
- (void)evalJs:(NSString*)js scheduledOnRunLoop:(BOOL)scheduledOnRunLoop { js = [NSString stringWithFormat: @"cordova.require('cordova/exec').nativeEvalAndFetch(function(){ %@ })", js]; if (scheduledOnRunLoop) { [self evalJsHelper:js]; } else { [self evalJsHelper2:js]; } }
- (void)evalJsHelper2:(NSString*)js
{
CDV_EXEC_LOG(@"Exec: evalling: %@",
[js substringToIndex:MIN([js length], 160)]);
NSString* commandsJSON = [_viewController.webView
stringByEvaluatingJavaScriptFromString:js];
if ([commandsJSON length] > 0) {
CDV_EXEC_LOG(@"Exec: Retrieved new exec messages
by chaining.");
}
[_commandQueue enqueCommandBatch:commandsJSON];
}
- (void)evalJsHelper:(NSString*)js
{
// Cycle the run-loop before executing the JS.
// This works around a bug where sometimes alerts()
within callbacks can cause
// dead-lock.
// If the commandQueue is currently executing,
then we know that it is safe to
// execute the callback immediately.
// Using (dispatch_get_main_queue()) does *not*
fix deadlocks for some reaon,
// but performSelectorOnMainThread: does.
if (![NSThread isMainThread] || !_commandQueue.currentlyExecuting)
{
[self performSelectorOnMainThread:@selector(evalJsHelper2:)
withObject:js
waitUntilDone:NO];
} else {
[self evalJsHelper2:js];
}
} |
Ôõô´®ÆðÀ´
ÏÈ¿´Ò»Ï Cordova JS ¶ËÇëÇó·½·¨µÄ¸ñʽ£º
// successCallback : ³É¹¦»Øµ÷·½·¨ // failCallback : ʧ°Ü»Øµ÷·½·¨ // server : ËùÒªÇëÇóµÄ·þÎñÃû×Ö // action : ËùÒªÇëÇóµÄ·þÎñ¾ßÌå²Ù×÷ // actionArgs : ÇëÇó²Ù×÷Ëù´øµÄ²ÎÊý cordova.exec(successCallback, failCallback, service, action, actionArgs); |
´«½øÀ´µÄÕâÎå¸ö²ÎÊý²¢²»ÊÇÖ±½Ó´«Ë͸øÔÉú´úÂëµÄ£¬Cordova JS ¶Ë»á×öÒÔϵĴ¦Àí£º
1.»áΪÿ¸öÇëÇóÉú³ÉÒ»¸ö½Ð callbackId µÄΨһ±êʶ£ºÕâ¸ö²ÎÊýÐè´«¸ø
Objective-C ¶Ë£¬Objective-C ´¦ÀíÍêºó£¬»á°Ñ callbackId Á¬Í¬´¦Àí½á¹ûÒ»Æð·µ»Ø¸ø
JS ¶Ë
2.ÒÔ callbackId Ϊ key£¬{success:successCallback,
fail:failCallback} Ϊ value£¬°ÑÕâ¸ö¼üÖµ¶Ô±£´æÔÚ JS ¶ËµÄ×ÖµäÀsuccessCallback
Óë failCallback ÕâÁ½¸ö²ÎÊý²»ÐèÒª´«¸ø Objective-C ¶Ë£¬Objective-C
·µ»Ø½á¹ûʱ´øÉÏ callbackId£¬JS ¶Ë¾Í¿ÉÒÔ¸ù¾Ý callbackId ÕÒµ½»Øµ÷·½·¨
3.ÿ´Î JS ÇëÇó£¬×îºó·¢µ½ Objective-C µÄÊý¾Ý°üÀ¨£ºcallbackId,
service, action, actionArgs
¹Ø¼ü´úÂëÈçÏ£º
function iOSExec() { ... // Éú³ÉÒ»¸ö callbackId µÄΨһ±êʶ£¬²¢°Ñ´Ë±êÖ¾Óë³É¹¦¡¢Ê§°Ü»Øµ÷·½·¨Ò»Æð±£´æÔÚ JS ¶Ë // 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);
// °Ñ callbackId£¬service£¬action£¬actionArgs ±£³Öµ½
commandQueue ÖÐ
// ÕâËĸö²ÎÊý¾ÍÊÇ×îºó·¢¸øÔÉú´úÂëµÄÊý¾Ý
var command = [callbackId, service, action, actionArgs];
commandQueue.push(JSON.stringify(command));
...
}
// »ñÈ¡ÇëÇóµÄÊý¾Ý£¬°üÀ¨ callbackId, service, action, actionArgs
iOSExec.nativeFetchMessages = function() {
// Each entry in commandQueue is a JSON string
already.
if (!commandQueue.length) {
return '';
}
var json = '[' + commandQueue.join(',') + ']';
commandQueue.length = 0;
return json;
}; |
ÔÉú´úÂëÄõ½ callbackId¡¢service¡¢action ¼° actionArgs ºó£¬»á×öÒÔϵĴ¦Àí£º
1.¸ù¾Ý service ²ÎÊýÕÒµ½¶ÔÓ¦µÄ²å¼þÀà
2.¸ù¾Ý action ²ÎÊýÕÒµ½²å¼þÀàÖжÔÓ¦µÄ´¦Àí·½·¨£¬²¢°Ñ actionArgs
×÷Ϊ´¦Àí·½·¨ÇëÇó²ÎÊýµÄÒ»²¿·Ö´«¸ø´¦Àí·½·¨
3.´¦ÀíÍê³Éºó£¬°Ñ´¦Àí½á¹û¼° callbackId ·µ»Ø¸ø JS ¶Ë£¬JS
¶ËÊÕµ½ºó»á¸ù¾Ý callbackId ÕÒµ½»Øµ÷·½·¨£¬²¢°Ñ´¦Àí½á¹û´«¸ø»Øµ÷·½·¨
¹Ø¼ü´úÂ룺
- (void)sendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId { CDV_EXEC_LOG(@"Exec(%@): Sending result. Status=%@", callbackId, result.status); // This occurs when there is are no win/fail callbacks for the call. if ([@"INVALID" isEqualToString : callbackId]) { return; } int status = [result.status intValue]; BOOL keepCallback = [result.keepCallback boolValue]; NSString* argumentsAsJSON = [result argumentsAsJSON];
// ½«ÇëÇóµÄ´¦Àí½á¹û¼° callbackId ͨ¹ýµ÷Óà JS ·½·¨·µ»Ø¸ø JS ¶Ë
NSString* js = [NSString stringWithFormat:
@"cordova.require('cordova/exec').nativeCallback('%@',%d,%@,%d)",
callbackId, status, argumentsAsJSON, keepCallback];
[self evalJsHelper:js];
} |
// ¸ù¾Ý callbackId ¼°ÊÇ·ñ³É¹¦±êʶ£¬ÕÒµ½»Øµ÷·½·¨£¬²¢°Ñ´¦Àí½á¹û´«¸ø»Øµ÷·½·¨ callbackFromNative: function(callbackId, success, status, args, keepCallback) { var callback = cordova.callbacks[callbackId]; if (callback) { if (success && status == cordova.callbackStatus.OK) { callback.success && callback.success.apply(null, args); } else if (!success) { callback.fail && callback.fail.apply(null, args); }
// Clear callback if not expecting any more
results
if (!keepCallback) {
delete cordova.callbacks[callbackId];
}
}
} |
ͨÐÅЧÂÊ
Cordova ÕâÌ×ͨÐÅЧÂʲ¢²»ËãµÍ¡£ÎÒʹÓà iPod Touch 4 Óë iPhone 5 ½øÐÐÕæ»ú²âÊÔ£ºJS
×öÒ»´ÎÇëÇó£¬Objective-C ÊÕµ½ÇëÇóºó²»×öÈκεĴ¦Àí£¬ÂíÉϰÑÇëÇóµÄÊý¾Ý·µ»Ø¸ø JS ¶Ë£¬ÕâÑùÄÜ´ó¸ÅµÄ²â³öÒ»À´Ò»ÍùµÄʱ¼ä(´Ó
JS ·¢³öÇëÇ󣬵½ JS ÊÕµ½½á¹ûµÄʱ¼ä)¡£Ã¿¸öÕæ»úÎÒ×öÁËÈý×é²âÊÔ£¬Ã¿×éÁ¬Ðø²âÊÔÊ®´Î£¬Ã¿×é²âÊÔǰÎÒ¶¼»á°Ñ»úÆ÷ÖØÆô£¬½á¹ûÈçÏ£º
iPod Touch 4(ʱ¼äµ¥Î»£ººÁÃë)£º

ÕâÈýÊ®´Î²âÊÔµÄÆ½¾ùʱ¼äÊÇ£º(11.0 + 15.2 + 13.2) / 3 = 13.13 ºÁÃë
iPhone 5(ʱ¼äµ¥Î»£ººÁÃë)

ÕâÈýÊ®´Î²âÊÔµÄÆ½¾ùʱ¼äÊÇ£º(2.7 + 2.8 + 2.7) / 3 = 2.73 ºÁÃë
ÕâͨÐŵÄЧÂÊËäÈ»±È²»ÉÏÔÉúµ÷ÔÉú£¬µ«ÊÇÒ²ÊÇÊôÓڿɽÓÊܵķ¶Î§ÁË¡£
|