Cordova - iOS 프레임 소스 분석
1. 뷰 디 드 로드 에서 무엇 을 했 는 지 살 펴 보 자.
- (void)viewDidLoad
{
[super viewDidLoad];
1. config.xml , 。
// Load settings
[self loadSettings];
2. cordova
NSString* backupWebStorageType = @"cloud"; // default value
id backupWebStorage = [self.settings cordovaSettingForKey:@"BackupWebStorage"];
if ([backupWebStorage isKindOfClass:[NSString class]]) {
backupWebStorageType = backupWebStorage;
}
[self.settings setCordovaSetting:backupWebStorageType forKey:@"BackupWebStorage"];
[CDVLocalStorage __fixupDatabaseLocationsWithBackupType:backupWebStorageType];
// // Instantiate the WebView ///
3. Cordova Webview,
if (!self.webView) {
[self createGapView];
}
// /
/*
* Fire up CDVLocalStorage to work-around WebKit storage limitations: on all iOS 5.1+ versions for local-only backups, but only needed on iOS 5.1 for cloud backup.
With minimum iOS 7/8 supported, only first clause applies.
*/
if ([backupWebStorageType isEqualToString:@"local"]) {
NSString* localStorageFeatureName = @"localstorage";
if ([self.pluginsMap objectForKey:localStorageFeatureName]) { // plugin specified in config
[self.startupPluginNames addObject:localStorageFeatureName];
}
}
4. config.xml , onload true
if ([self.startupPluginNames count] > 0) {
[CDVTimer start:@"TotalPluginStartup"];
for (NSString* pluginName in self.startupPluginNames) {
[CDVTimer start:pluginName];
[self getCommandInstance:pluginName];
[CDVTimer stop:pluginName];
}
[CDVTimer stop:@"TotalPluginStartup"];
}
// /
5. url
NSURL* appURL = [self appUrl];
6. webView userAgent , url
[CDVUserAgentUtil acquireLock:^(NSInteger lockToken) {
_userAgentLockToken = lockToken;
[CDVUserAgentUtil setUserAgent:self.userAgent lockToken:lockToken];
if (appURL) {
NSURLRequest* appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
[self.webViewEngine loadRequest:appReq];
} else {
NSString* loadErr = [NSString stringWithFormat:@"ERROR: Start Page at '%@/%@' was not found.", self.wwwFolderName, self.startPage];
NSLog(@"%@", loadErr);
NSURL* errorUrl = [self errorURL];
if (errorUrl) {
errorUrl = [NSURL URLWithString:[NSString stringWithFormat:@"?error=%@", [loadErr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] relativeToURL:errorUrl];
NSLog(@"%@", [errorUrl absoluteString]);
[self.webViewEngine loadRequest:[NSURLRequest requestWithURL:errorUrl]];
} else {
NSString* html = [NSString stringWithFormat:@" %@ ", loadErr];
[self.webViewEngine loadHTMLString:html baseURL:nil];
}
}
}];
}
다음 단계 에서 구체 적 으로 어떻게 실현 되 었 는 지 분석한다.
2. 먼저 프로필 을 불 러 올 지, 코드 를 볼 지:
- (void)loadSettings
{
1.config.xml
CDVConfigParser* delegate = [[CDVConfigParser alloc] init];
[self parseSettingsWithParser:delegate];
2. self, CDVViewController, pluginsMap xml ,key feature,value 。startupPluginNames onload true , ,settings xml web , 。
// Get the plugin dictionary, whitelist and settings from the delegate.
self.pluginsMap = delegate.pluginsDict;
self.startupPluginNames = delegate.startupPluginNames;
self.settings = delegate.settings;
3. wwwFolderName www,wwwFolderName 。
// And the start folder/page.
if(self.wwwFolderName == nil){
self.wwwFolderName = @"www";
}
4.startPage , xml , index.html。
if(delegate.startPage && self.startPage == nil){
self.startPage = delegate.startPage;
}
if (self.startPage == nil) {
self.startPage = @"index.html";
}
// Initialize the plugin objects dict.
self.pluginObjects = [[NSMutableDictionary alloc] initWithCapacity:20];
}
3. Cordova 의 webview 를 설정 하 는 것 은 분석 에 중심 을 두 는 것 이 중요 합 니 다.
- (UIView*)newCordovaViewWithFrame:(CGRect)bounds
{
1. webView , CDVViewController webView , 。 , , UIWebView , WKWebView, 。
NSString* defaultWebViewEngineClass = @"CDVUIWebViewEngine";
NSString* webViewEngineClass = [self.settings cordovaSettingForKey:@"CordovaWebViewEngine"];
if (!webViewEngineClass) {
webViewEngineClass = defaultWebViewEngineClass;
}
2. webView
if (NSClassFromString(webViewEngineClass)) {
self.webViewEngine = [[NSClassFromString(webViewEngineClass) alloc] initWithFrame:bounds];
3. webEngine nil, protocol, url, , 。
// if a webView engine returns nil (not supported by the current iOS version) or doesn't conform to the protocol, or can't load the request, we use UIWebView
if (!self.webViewEngine || ![self.webViewEngine conformsToProtocol:@protocol(CDVWebViewEngineProtocol)] || ![self.webViewEngine canLoadRequest:[NSURLRequest requestWithURL:self.appUrl]]) {
self.webViewEngine = [[NSClassFromString(defaultWebViewEngineClass) alloc] initWithFrame:bounds];
}
} else {
self.webViewEngine = [[NSClassFromString(defaultWebViewEngineClass) alloc] initWithFrame:bounds];
}
4. webView
if ([self.webViewEngine isKindOfClass:[CDVPlugin class]]) {
[self registerPlugin:(CDVPlugin*)self.webViewEngine withClassName:webViewEngineClass];
}
5. webView
return self.webViewEngine.engineWebView;
}
이 부분 은 약간 추상 적 입 니 다. 사실은 프로 토 콜 을 대상 으로 하 는 프로 그래 밍 사상 을 바탕 으로 인터페이스 와 시 도 를 분리 시 켰 습 니 다. id webViewEngine 은 실제 적 으로 id 유형 을 가리 키 고 CDVWebViewEngineProtocol 프로 토 콜 의 대상 을 따 랐 습 니 다. 즉, CDVWebViewEngineProtocol 이 누 출 된 인 터 페 이 스 를 실현 할 수 있 습 니 다.이렇게 하면 우 리 는 추상 류 가 이 협 의 를 따 르 게 하면 협의 에서 정 의 된 방법 과 속성 을 실현 하여 인터페이스 분 리 를 실현 할 수 있다.같은 이치 로 webView Engine 추상 류 는 겉으로 볼 때 webview 로 실제 적 으로 webView 를 추출 하여 분리 시 키 고 결합 을 실현 합 니 다.
4. 웹 뷰 엔진 대상 이 무엇 을 했 는 지 자세히 말씀 드 리 겠 습 니 다.코드 를 보 든 가.
- (instancetype)initWithFrame:(CGRect)frame
{
1.
self = [super init];
if (self) {
2. WebView, webView, DLPanableWebView webView, web DLPanableWebView , 。
Class WebClass = NSClassFromString(@"DLPanableWebView");
if ([[WebClass class] isSubclassOfClass:[UIWebView class]]) {
self.engineWebView = [[WebClass alloc] initWithFrame:frame];
} else {
self.engineWebView = [[UIWebView alloc] initWithFrame:frame];
}
NSLog(@"Using UIWebView");
}
return self;
}
。
- (void)pluginInitialize
{
// viewController would be available now. we attempt to set all possible delegates to it, by default
1. web。
UIWebView* uiWebView = (UIWebView*)_engineWebView;
2. Controller UIWebView , , , web controller 。
if ([self.viewController conformsToProtocol:@protocol(UIWebViewDelegate)]) {
self.uiWebViewDelegate = [[CDVUIWebViewDelegate alloc] initWithDelegate:(id )self.viewController];
uiWebView.delegate = self.uiWebViewDelegate;
} else {
3. controller , 。 HWebViewDelegate, web , 。
self.navWebViewDelegate = [[CDVUIWebViewNavigationDelegate alloc] initWithEnginePlugin:self];
Class TheClass = NSClassFromString(@"HWebViewDelegate");
if ([TheClass isSubclassOfClass:[CDVUIWebViewDelegate class]]) {
self.uiWebViewDelegate = [[TheClass alloc] initWithDelegate:self.navWebViewDelegate];
} else {
self.uiWebViewDelegate = [[CDVUIWebViewDelegate alloc] initWithDelegate:self.navWebViewDelegate];
}
// end
uiWebView.delegate = self.uiWebViewDelegate;
}
[self updateSettings:self.commandDelegate.settings];
}
5. 지금까지 플러그 인 설정 과 로 딩 이 완료 되 었 습 니 다. webView 의 구체 적 인 실현 과 프 록 시 설정 도 완성 되 었 습 니 다. 그러면 네 이 티 브 와 js 의 구체 적 인 상호작용 을 말씀 드 리 겠 습 니 다. 주로 네 이 티 브 엔 드 가 무엇 을 했 는 지 말씀 드 리 겠 습 니 다.이것 은 CDVUIWebView Navigation Delegate 클래스 에서 웹 프 록 시 를 실현 하 는 것 이 며, 위 에서 웹 뷰 를 설정 할 때 프 록 시 로 설정 하 는 것 입 니 다.여기 서 의 실현 은 바로 상호작용 의 중요 한 것 이다. 그러면 우 리 는 상세 하 게 말 하 자.
- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
{
1. url
NSURL* url = [request URL];
2.
CDVViewController* vc = (CDVViewController*)self.enginePlugin.viewController;
3. url scheme gap
if ([[url scheme] isEqualToString:@"gap"]) {
4. , 。
[vc.commandQueue fetchCommandsFromJs];
[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;
}
여기 서 두 가지 방법 을 중점적으로 분석한다. [vc. com mand Queue fetch Commands FromJs];그리고 [vc. comandQueue execute Pending];우리 가 막 은 구체 적 인 실현 이기 도 하 다.코드 를 보 든 가.
- (void)fetchCommandsFromJs
{
__weak CDVCommandQueue* weakSelf = self;
NSString* js = @"cordova.require('cordova/exec').nativeFetchMessages()";
1. jsBridge js ,js
[_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);
2. 。
[weakSelf enqueueCommandBatch:queuedCommandsJSON];
// this has to be called here now, because fetchCommandsFromJs is now async (previously: synchronous)
3.
[self executePending];
}
}];
}
- (void)enqueueCommandBatch:(NSString*)batchJSON
{
1. 。
if ([batchJSON length] > 0) {
NSMutableArray* commandBatchHolder = [[NSMutableArray alloc] init];
2. queue 。
[_queue addObject:commandBatchHolder];
3. json 4M , 。
if ([batchJSON length] < JSON_SIZE_FOR_MAIN_THREAD) {
4. commandBatchHolder 。
[commandBatchHolder addObject:[batchJSON cdv_JSONObject]];
} else {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() {
NSMutableArray* result = [batchJSON cdv_JSONObject];
5. 。
@synchronized(commandBatchHolder) {
[commandBatchHolder addObject:result];
}
6. executePending
[self performSelectorOnMainThread:@selector(executePending) withObject:nil waitUntilDone:NO];
});
}
}
}
6. 지금까지 우 리 는 설 정 된 플러그 인, webView, js 단 에서 전 달 된 인 자 를 받 았 습 니 다. 마지막 단계 가 남 았 습 니 다. 인 자 는 플러그 인 에 어떻게 호출 되 었 습 니까?코드
- (void)executePending
{
1. executePending , 。
if (_startExecutionTime > 0) {
return;
}
@try {
_startExecutionTime = [NSDate timeIntervalSinceReferenceDate];
2. queue , 。
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];
}
3. queue 。
while ([commandBatch count] > 0) {
4. 。
@autoreleasepool {
5. , 。
NSArray* jsonEntry = [commandBatch cdv_dequeue];
if ([commandBatch count] == 0) {
6. 。
[_queue removeObjectAtIndex:0];
}
7. CDVInvokedUrlCommand , CDVInvokedUrlCommand 。
CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:jsonEntry];
CDV_EXEC_LOG(@"Exec(%@): Calling %@.%@", command.callbackId, command.className, command.methodName);
8. 。
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
}
}
9. runloop , runloop , UI 。
// 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;
}
}
- (BOOL)execute:(CDVInvokedUrlCommand*)command
{
if ((command.className == nil) || (command.methodName == nil)) {
NSLog(@"ERROR: Classname and/or methodName not found for command.");
return NO;
}
1. native 。
CDVPlugin* obj = [_viewController.commandDelegate getCommandInstance:command.className];
2. CDVPlugin。
if (!([obj isKindOfClass:[CDVPlugin class]])) {
NSLog(@"ERROR: Plugin '%@' not found, or is not a CDVPlugin. Check your plugin mapping in config.xml.", command.className);
return NO;
}
BOOL retVal = YES;
double started = [[NSDate date] timeIntervalSince1970] * 1000.0;
// Find the proper selector to call.
NSString* methodName = [NSString stringWithFormat:@"%@:", command.methodName];
3. 。
SEL normalSelector = NSSelectorFromString(methodName);
4. 。
if ([obj respondsToSelector:normalSelector]) {
// [obj performSelector:normalSelector withObject:command];
((void (*)(id, SEL, id))objc_msgSend)(obj, normalSelector, command);
} else {
// There's no method to call, so throw an error.
NSLog(@"ERROR: Method '%@' not defined in Plugin '%@'", methodName, command.className);
retVal = NO;
}
double elapsed = [[NSDate date] timeIntervalSince1970] * 1000.0 - started;
if (elapsed > 10) {
NSLog(@"THREAD WARNING: ['%@'] took '%f' ms. Plugin should use a background thread.", command.className, elapsed);
}
return retVal;
}
여기까지 전체 플러그 인의 호출 과정 이 끝 났 습 니 다. plugin 을 생 성 합 니 다. 프레임 워 크 는 공장 의 디자인 모델 을 바탕 으로 서로 다른 유형의 이름 을 통 해 CDVPlugin 의 서로 다른 대상 을 계승 한 다음 에 해당 하 는 plugin 대상 에서 해당 하 는 방법 을 수행 합 니 다.
다음으로 전송:https://www.cnblogs.com/byxixiblogs/p/9980533.html
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Angular vs Svelte - 카드 구성요소이전 게시물 중 일부를 확인하면 최근에 Svelte 및 Sapper로 몇 가지 실험을 하고 있음을 알 수 있습니다. 몇 년 동안 Angular로 작업했고 지금은 Svelte를 배우고 있기 때문에 일부 구성 요소를 A...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.