1.Cordova类图
2.插件管理及插件
控制器基类 CDVViewController,插件管理数据结构
@interface CDVViewController ()
...
@property (nonatomic, readwrite, strong) NSMutableDictionary* pluginObjects;
@property (nonatomic, readwrite, strong) NSMutableArray* startupPluginNames;
@property (nonatomic, readwrite, strong) NSDictionary* pluginsMap;
...
@end
控制器基类 CDVViewController,插件管理操作处理
分为初始化注册插件及取插件名,来得到插件,从而调用相应的插件方法,去执行js
@interface CDVViewController : UIViewController <CDVScreenOrientationDelegate>
...
- (id)getCommandInstance:(NSString*)pluginName;
- (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className;
- (void)registerPlugin:(CDVPlugin*)plugin withPluginName:(NSString*)pluginName;
...
3.js调用app
刷新webView, 产生回调
- (BOOL)webView:(UIWebView )webView shouldStartLoadWithRequest:(NSURLRequest )request navigationType:(UIWebViewNavigationType)navigationType;
调用 fetchCommandsFromJs 执行 @”cordova.require(‘cordova/exec’).nativeFetchMessages()”获取js返回信息
[CDVCommandQueue fetchCommandsFromJs]
- (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;
}
...
}
#0 0x00000001002095f0 in -[CDVCommandQueue enqueueCommandBatch:]
#1 0x0000000100209c70 in -[CDVCommandQueue fetchCommandsFromJs]_block_invoke
#2 0x000000010020cfec in -[CDVUIWebViewEngine evaluateJavaScript:completionHandler:]
#3 0x0000000100209af0 in -[CDVCommandQueue fetchCommandsFromJs]
#4 0x0000000100208c6c in -[CDVUIWebViewNavigationDelegate webView:shouldStartLoadWithRequest:navigationType:]
#5 0x00000001001feb7c in -[CDVUIWebViewDelegate webView:shouldStartLoadWithRequest:navigationType:]
#6 0x0000000196e14d34 in -[UIWebView webView:decidePolicyForNavigationAction:request:frame:decisionListener:] ()
#7 0x0000000190ad0e80 in __invoking___ ()
#8 0x00000001909c62c4 in -[NSInvocation invoke] ()
#9 0x00000001909cae9c in -[NSInvocation invokeWithTarget:] ()
#10 0x0000000196858820 in -[_WebSafeForwarder forwardInvocation:] ()
#11 0x0000000190aced54 in ___forwarding___ ()
#12 0x00000001909cad4c in _CF_forwarding_prep_0 ()
#13 0x0000000190ad0e80 in __invoking___ ()
#14 0x00000001909c62c4 in -[NSInvocation invoke] ()
#15 0x00000001956a0be8 in HandleDelegateSource(void*) ()
#16 0x0000000190a7942c in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ ()
#17 0x0000000190a78d9c in __CFRunLoopDoSources0 ()
#18 0x0000000190a769a8 in __CFRunLoopRun ()
#19 0x00000001909a6da4 in CFRunLoopRunSpecific ()
#20 0x0000000192410074 in GSEventRunModal ()
#21 0x0000000196c61058 in UIApplicationMain ()
#22 0x00000001000ff544 in main
再根据js返回信息,CDVCommandQueue动态创建本地插件,识别方法,进行调用
- (void)fetchCommandsFromJs
{
__weak CDVCommandQueue* weakSelf = self;
NSString* js = @"cordova.require('cordova/exec').nativeFetchMessages()";
[_viewController.webViewEngine evaluateJavaScript:js
completionHandler:^(id obj, NSError* error) {
if ((error == nil) && [obj isKindOfClass:[NSString class]]) {
NSString* queuedCommandsJSON = (NSString*)obj;
CDV_EXEC_LOG(@"Exec: Flushed JS->native queue (hadCommands=%d).", [queuedCommandsJSON length] > 0);
[weakSelf enqueueCommandBatch:queuedCommandsJSON];
// this has to be called here now, because fetchCommandsFromJs is now async (previously: synchronous)
[self executePending];
}
}];
}
- (void)executePending
{
// Make us re-entrant-safe.
if (_startExecutionTime > 0) {
return;
}
@try {
_startExecutionTime = [NSDate timeIntervalSinceReferenceDate];
while ([_queue count] > 0) {
NSMutableArray* commandBatchHolder = _queue[0];
NSMutableArray* commandBatch = nil;
@synchronized(commandBatchHolder) {
// If the next-up command is still being decoded, wait for it.
if ([commandBatchHolder count] == 0) {
break;
}
commandBatch = commandBatchHolder[0];
}
while ([commandBatch count] > 0) {
@autoreleasepool {
// Execute the commands one-at-a-time.
NSArray* jsonEntry = [commandBatch cdv_dequeue];
if ([commandBatch count] == 0) {
[_queue removeObjectAtIndex:0];
}
CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:jsonEntry];
CDV_EXEC_LOG(@"Exec(%@): Calling %@.%@", command.callbackId, command.className, command.methodName);
if (![self execute:command]) {
#ifdef DEBUG
NSString* commandJson = [jsonEntry cdv_JSONString];
static NSUInteger maxLogLength = 1024;
NSString* commandString = ([commandJson length] > maxLogLength) ?
[NSString stringWithFormat : @"%@[...]", [commandJson substringToIndex:maxLogLength]] :
commandJson;
DLog(@"FAILED pluginJSON = %@", commandString);
#endif
}
}
// Yield if we're taking too long.
if (([_queue count] > 0) && ([NSDate timeIntervalSinceReferenceDate] - _startExecutionTime > MAX_EXECUTION_TIME)) {
[self performSelector:@selector(executePending) withObject:nil afterDelay:0];
return;
}
}
}
} @finally
{
_startExecutionTime = 0;
}
}
4, app 调用js
被动调用过程
调用增加热点[cordova_plug_krpanoedit addhotspot:]本质是上执行js @”cordova.require(‘cordova/exec’).nativeCallback(‘cordova_plug_krpanoedit1493891283’,1,{“y”:”26.3451”,”title”:”美景”,”name”:”hostspot0”,”x”:”120.073”,”scale”:”0.4”},0, 1)”
#0 0x000000010020cfc4 in -[CDVUIWebViewEngine evaluateJavaScript:completionHandler:]
#1 0x00000001002001a0 in -[CDVCommandDelegateImpl evalJsHelper2:]
#2 0x000000010020054c in -[CDVCommandDelegateImpl evalJsHelper:]
#3 0x0000000100200960 in -[CDVCommandDelegateImpl sendPluginResult:callbackId:]
#4 0x0000000100115448 in -[cordova_plug_krpanoedit addhotspot:]
主动调用,在应用程序中增加按钮,用户操作按钮来激活相应的javascript操作的情况
在CDVCommandDelegateImpl.h中增加下面的公开方法,相当于是直接调用webView的文件来执行javascript,不经过Cordova框架去查找插件,由插件再来调用javascript函数。
- (void)evalJsHelper:(NSString*)js;
@interface CDVCommandDelegateImpl : NSObject <CDVCommandDelegate>{
@private
__weak CDVViewController* _viewController;
NSRegularExpression* _callbackIdPattern;
@protected
__weak CDVCommandQueue* _commandQueue;
BOOL _delayResponses;
}
- (id)initWithViewController:(CDVViewController*)viewController;
- (void)flushCommandQueueWithDelayedJs;
- (void)evalJsHelper:(NSString*)js;
@end
调用示例
//控制器
@implementation MainViewController
...
- (void)viewDidLoad
{
[super viewDidLoad];
UIButton *btn;
btn = [[UIButton alloc] init];
btn.frame = CGRectMake(100, 100, 80, 30);
[btn setTitle:@"hotspot" forState:UIControlStateNormal ];
[self.view addSubview:btn];
[btn addTarget:self action:@selector(action:) forControlEvents:UIControlEventTouchUpInside];
// Do any additional setup after loading the view from its nib.
}
- (void) action:(UIButton*)sender{
cordova_plug_krpanoedit *kr;
kr = [self getCommandInstance:@"cordova_plug_krpanoedit"];
if(kr){
[kr appAddhotspot:nil];
}
}
...
@end
//插件
@implementation cordova_plug_krpanoedit
...
- (void) appAddhotspot:(NSDictionary*)dic {
[self.commandDelegate evalJs:@"hotspot();"];
}
...
@end
app主动调用,目前想到的是这样的一个方法,如果有更好的办法,可以留言,我补充上去。
5.别人的梳理文章