Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Modeler   Code  
»áÔ±   
 
   
 
 
     
   
 ¶©ÔÄ
  ¾èÖú
dzÎö Cordova for iOS
 
×÷Õߣºzhenby's blog ·¢²¼ÓÚ 2015-8-10
  2321  次浏览      32
 

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 ºÁÃë

ÕâͨÐŵÄЧÂÊËäÈ»±È²»ÉÏÔ­Éúµ÷Ô­Éú£¬µ«ÊÇÒ²ÊÇÊôÓڿɽÓÊܵķ¶Î§ÁË¡£

   
2321 ´Îä¯ÀÀ       32
 
Ïà¹ØÎÄÕÂ

ÊÖ»úÈí¼þ²âÊÔÓÃÀýÉè¼ÆÊµ¼ù
ÊÖ»ú¿Í»§¶ËUI²âÊÔ·ÖÎö
iPhoneÏûÏ¢ÍÆËÍ»úÖÆÊµÏÖÓë̽ÌÖ
AndroidÊÖ»ú¿ª·¢£¨Ò»£©
 
Ïà¹ØÎĵµ

Android_UI¹Ù·½Éè¼Æ½Ì³Ì
ÊÖ»ú¿ª·¢Æ½Ì¨½éÉÜ
androidÅÄÕÕ¼°ÉÏ´«¹¦ÄÜ
Android½²ÒåÖÇÄÜÊÖ»ú¿ª·¢
Ïà¹Ø¿Î³Ì

Android¸ß¼¶Òƶ¯Ó¦ÓóÌÐò
Androidϵͳ¿ª·¢
AndroidÓ¦Óÿª·¢
ÊÖ»úÈí¼þ²âÊÔ
×îл¼Æ»®
DeepSeekÔÚÈí¼þ²âÊÔÓ¦ÓÃʵ¼ù 4-12[ÔÚÏß]
DeepSeek´óÄ£ÐÍÓ¦Óÿª·¢Êµ¼ù 4-19[ÔÚÏß]
UAF¼Ü¹¹ÌåϵÓëʵ¼ù 4-11[±±¾©]
AIÖÇÄÜ»¯Èí¼þ²âÊÔ·½·¨Óëʵ¼ù 5-23[ÉϺ£]
»ùÓÚ UML ºÍEA½øÐзÖÎöÉè¼Æ 4-26[±±¾©]
ÒµÎñ¼Ü¹¹Éè¼ÆÓ뽨ģ 4-18[±±¾©]

androidÈË»ú½çÃæÖ¸ÄÏ
AndroidÊÖ»ú¿ª·¢£¨Ò»£©
AndroidÊÖ»ú¿ª·¢£¨¶þ£©
AndroidÊÖ»ú¿ª·¢£¨Èý£©
AndroidÊÖ»ú¿ª·¢£¨ËÄ£©
iPhoneÏûÏ¢ÍÆËÍ»úÖÆÊµÏÖ̽ÌÖ
ÊÖ»úÈí¼þ²âÊÔÓÃÀýÉè¼ÆÊµ¼ù
ÊÖ»ú¿Í»§¶ËUI²âÊÔ·ÖÎö
ÊÖ»úÈí¼þ×Ô¶¯»¯²âÊÔÑо¿±¨¸æ

Android¸ß¼¶Òƶ¯Ó¦ÓóÌÐò
AndroidÓ¦Óÿª·¢
Androidϵͳ¿ª·¢
ÊÖ»úÈí¼þ²âÊÔ
ǶÈëʽÈí¼þ²âÊÔ
AndroidÈí¡¢Ó²¡¢ÔÆÕûºÏ

ÁìÏÈIT¹«Ë¾ android¿ª·¢Æ½Ì¨×î¼Ñʵ¼ù
±±¾© Android¿ª·¢¼¼Êõ½ø½×
ijÐÂÄÜÔ´ÁìÓòÆóÒµ Android¿ª·¢¼¼Êõ
ijº½Ì칫˾ Android¡¢IOSÓ¦ÓÃÈí¼þ¿ª·¢
°¢¶û¿¨ÌØ LinuxÄÚºËÇý¶¯
°¬Ä¬Éú ǶÈëʽÈí¼þ¼Ü¹¹Éè¼Æ
Î÷ÃÅ×Ó Ç¶Èëʽ¼Ü¹¹Éè¼Æ