blob: a85518c2c82631a2945d5b0f714c422f5e0fe010 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#import "WXPrerenderManager.h"
#import "WXConfigCenterProtocol.h"
#import "WXModuleMethod.h"
#import "WXBridgeManager.h"
#import "WXSDKInstance_private.h"
#import "WXBridgeManager.h"
#import "WXSDKEngine.h"
#import "WXUtility.h"
static NSString *const MSG_PRERENDER_INTERNAL_ERROR = @"internal_error";
static NSString *const MSG_PRERENDER_SUCCESS = @"success";
@interface WXPrerenderTask:NSObject
@property (nonatomic, strong) WXSDKInstance *instance;
@property (nonatomic, strong) UIView *view;
@property (nonatomic, copy) NSString *parentInstanceId;
@property (nonatomic, copy) NSString *url;
@property (nonatomic, strong) NSMutableArray *moduleTasks;
@property (nonatomic, strong) NSError *error;
@property (nonatomic, assign) WXState state;
@property (nonatomic, strong) NSDate *beginDate;
@property (nonatomic) long long cacheTime;
@property (nonatomic) BOOL isCache; // if set cache , the cachetime is no use.
@end
@implementation WXPrerenderTask
@end
@interface WXPrerenderManager()
@property (nonatomic, strong) NSMutableArray *cachedUrlList;
@property (nonatomic, strong) dispatch_queue_t queue;
@property (nonatomic, strong) NSMutableDictionary<NSString *, WXPrerenderTask*> *prerenderTasks;
@property (nonatomic) NSInteger maxCacheNumber;
@end
@implementation WXPrerenderManager
+ (instancetype) sharedInstance{
static WXPrerenderManager *instance = nil;
static dispatch_once_t once;
dispatch_once(&once, ^{
instance = [[WXPrerenderManager alloc] initPrivate];
});
return instance;
}
- (instancetype) initPrivate{
self = [super init];
if(self){
self.cachedUrlList = [[NSMutableArray alloc] init];
self.queue = dispatch_queue_create("Prerender", DISPATCH_QUEUE_SERIAL);
self.prerenderTasks = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void) dealloc{
self.cachedUrlList = nil;
self.prerenderTasks = nil;
}
+ (void) addTask:(NSString *)url instanceId:(NSString *)instanceId callback:(WXModuleKeepAliveCallback)callback{
NSURL *newUrl = [NSURL URLWithString:url];
if(!newUrl){
if(callback){
callback(@{@"url":url,@"message":MSG_PRERENDER_INTERNAL_ERROR,@"result":@"error"}, NO);
}
return;
}
WXPrerenderManager *manager = [WXPrerenderManager sharedInstance];
__weak WXPrerenderManager *weakSelf = manager;
dispatch_async(manager.queue, ^{
[weakSelf prerender:newUrl instanceId:instanceId isCache:NO callback:callback];
});
}
+ (void) addGlobalTask:(NSString *) url callback:(WXModuleKeepAliveCallback)callback
{
NSURL *newUrl = [NSURL URLWithString:url];
if(!newUrl){
if(callback){
callback(@{@"url":url,@"message":MSG_PRERENDER_INTERNAL_ERROR,@"result":@"error"}, NO);
}
return;
}
WXPrerenderManager *manager = [WXPrerenderManager sharedInstance];
__weak WXPrerenderManager *weakSelf = manager;
dispatch_async(manager.queue, ^{
[weakSelf prerender:newUrl instanceId:@"" isCache:YES callback:callback];
});
}
-(BOOL)isSwitchOn
{
BOOL switchOn = YES; // defautle YES
id configCenter = [WXSDKEngine handlerForProtocol:@protocol(WXConfigCenterProtocol)];
if ([configCenter respondsToSelector:@selector(configForKey:defaultValue:isDefault:)]) {
id switchOnValue = [configCenter configForKey:@"iOS_weex_prerender_config.is_switch_on" defaultValue:@true isDefault:NULL];
if(switchOnValue){
switchOn = [switchOnValue boolValue];
}
if(!switchOn){
return NO;
}
}
return YES;
}
- (void) prerender:(NSURL *)url instanceId:(NSString *)instanceId isCache:(BOOL)isCache callback:(WXModuleKeepAliveCallback) callback{
NSString *str = url.absoluteString;
if(str.length==0){
if(callback){
callback(@{@"url":[url absoluteString],@"message":MSG_PRERENDER_INTERNAL_ERROR,@"result":@"error"}, NO);
}
return;
}
WXPrerenderTask *task = [WXPrerenderTask new];
id configCenter = [WXSDKEngine handlerForProtocol:@protocol(WXConfigCenterProtocol)];
if(![self isSwitchOn]){
if(callback){
callback(@{@"url":[url absoluteString],@"message":MSG_PRERENDER_INTERNAL_ERROR,@"result":@"error"}, NO);
}
return;
}
task.beginDate = [NSDate date];
task.cacheTime = 300000;
if ([configCenter respondsToSelector:@selector(configForKey:defaultValue:isDefault:)]) {
long long time = [[configCenter configForKey:@"iOS_weex_prerender_config.cacheTime" defaultValue:@300000 isDefault:NULL] longLongValue];
if(time){
task.cacheTime = time;
}
}
self.maxCacheNumber = 5;
if ([configCenter respondsToSelector:@selector(configForKey:defaultValue:isDefault:)]) {
NSInteger max = [[configCenter configForKey:@"iOS_weex_prerender_config.max_cache_num" defaultValue:@5 isDefault:NULL] integerValue];
if(max){
self.maxCacheNumber = max;
}
}
if(self.prerenderTasks && self.prerenderTasks.count<self.maxCacheNumber){
[self.prerenderTasks setObject:task forKey:[WXPrerenderManager getTaskKeyFromUrl:url.absoluteString]];
WXPerformBlockOnMainThread(^{
WXSDKInstance *instance = [[WXSDKInstance alloc] init];
instance.needPrerender = YES;
task.instance = instance;
task.parentInstanceId = instanceId;
task.url = url.absoluteString;
task.isCache = isCache;
WXPrerenderManager *manager = [WXPrerenderManager sharedInstance];
__weak typeof(self) weakSelf = manager;
instance.onCreate = ^(UIView *view) {
WXPrerenderTask *task = [weakSelf.prerenderTasks objectForKey:[WXPrerenderManager getTaskKeyFromUrl:url.absoluteString]];
task.view = view;
if(task){
[weakSelf.prerenderTasks setObject:task forKey:[WXPrerenderManager getTaskKeyFromUrl:url.absoluteString]];
}
};
instance.onFailed = ^(NSError *error) {
WXPrerenderTask *task = [weakSelf.prerenderTasks objectForKey:[WXPrerenderManager getTaskKeyFromUrl:url.absoluteString]];
task.error = error;
if(task){
[weakSelf.prerenderTasks setObject:task forKey:[WXPrerenderManager getTaskKeyFromUrl:url.absoluteString]];
}
};
[instance renderWithURL:url options:@{@"bundleUrl":url.absoluteString} data:nil];
});
if(callback){
callback(@{@"url":url.absoluteString,@"message":MSG_PRERENDER_SUCCESS,@"result":@"success"}, NO);
}
}
}
+ (BOOL)isTaskReady:(NSString *)url{
return [[WXPrerenderManager sharedInstance]isTaskReady:url];
}
- (BOOL)isTaskReady:(NSString *)url
{
if( !url ||url.length == 0){
return NO;
}
WXPrerenderTask *task = [self.prerenderTasks objectForKey:[WXPrerenderManager getTaskKeyFromUrl:url]];
if(!task ){
return NO;
}
if(![self isSwitchOn]){
return NO;
}
// compare cache time with begin time
NSTimeInterval time = [[NSDate date] timeIntervalSinceDate:task.beginDate];
if(time > task.cacheTime && !task.isCache){
return NO;
}
if(!task.view) // view not exist ,not prerender
{
return NO;
}
if(task ){
return YES;
}
return NO;
}
+ (BOOL)isTaskExist:(NSString *)url{
if( !url ||url.length == 0){
return NO;
}
id configCenter = [WXSDKEngine handlerForProtocol:@protocol(WXConfigCenterProtocol)];
if ([configCenter respondsToSelector:@selector(configForKey:defaultValue:isDefault:)]) {
BOOL switchOn = NO; // defautle NO
id switchOnValue = [configCenter configForKey:@"iOS_weex_prerender_config.is_switch_on" defaultValue:@YES isDefault:NULL];
if(switchOnValue){
switchOn = [switchOnValue boolValue];
}
if(!switchOn){
return NO;
}
id urlsValue = [configCenter configForKey:@"iOS_weex_prerender_config.urls" defaultValue:NULL isDefault:NULL];
if(urlsValue){
NSData *data = [urlsValue dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
NSArray *urls = [WXUtility JSONObject:data error:&error];
if(urls && [urls count]>0){
for (NSString *configUrl in urls) {
if(configUrl && [[WXPrerenderManager getTaskKeyFromUrl:configUrl] isEqualToString:[WXPrerenderManager getTaskKeyFromUrl:url]]) {
return YES;
}
}
}
}
}
return NO;
}
- (BOOL)isTaskExist:(NSString *)url
{
if( !url ||url.length == 0){
return NO;
}
WXPrerenderTask *task = [self.prerenderTasks objectForKey:[WXPrerenderManager getTaskKeyFromUrl:url]];
if(task ){
return YES;
}
return NO;
}
+ (void)renderFromCache:(NSString *)url
{
WXPrerenderManager *manager = [WXPrerenderManager sharedInstance];
if([manager isTaskReady:url])
{
WXPrerenderTask *task = [manager.prerenderTasks objectForKey:[WXPrerenderManager getTaskKeyFromUrl:url]];
UIView *view = [self viewFromUrl:url];
NSError *error = [self errorFromUrl:url];
if(task.instance.onCreate){
task.instance.onCreate(view);
}
if(error && task.instance.onFailed){
task.instance.onFailed(error);
}
WXPerformBlockOnComponentThread(^{
[task.instance.componentManager startComponentTasks];
[task.instance.componentManager executePrerenderUITask:url];
task.instance.needPrerender = NO;
});
WXPerformBlockOnBridgeThreadForInstance(^{
[WXPrerenderManager excuteModuleTasksForUrl:url];
}, task.instance.instanceId);
}
}
+ (UIView *)viewFromUrl:(NSString *)url
{
WXPrerenderManager *manager = [WXPrerenderManager sharedInstance];
WXPrerenderTask *task = [manager.prerenderTasks objectForKey:[WXPrerenderManager getTaskKeyFromUrl:url]];
return task.view;
}
+ (NSError *)errorFromUrl:(NSString *)url
{
WXPrerenderManager *manager = [WXPrerenderManager sharedInstance];
WXPrerenderTask *task = [manager.prerenderTasks objectForKey:[WXPrerenderManager getTaskKeyFromUrl:url]];
return task.error;
}
+ (id )instanceFromUrl:(NSString *)url
{
WXPrerenderManager *manager = [WXPrerenderManager sharedInstance];
WXPrerenderTask *task = [manager.prerenderTasks objectForKey:[WXPrerenderManager getTaskKeyFromUrl:url]];
return task.instance;
}
+ (void)removePrerenderTaskforUrl:(NSString *)url
{
if (url.length > 0) {
WXPrerenderManager *manager = [WXPrerenderManager sharedInstance];
if(manager.prerenderTasks && [manager.prerenderTasks count]>0){
WXPrerenderTask *task = [manager.prerenderTasks objectForKey:[WXPrerenderManager getTaskKeyFromUrl:url]];
if(task){
[manager.prerenderTasks removeObjectForKey:[WXPrerenderManager getTaskKeyFromUrl:url]];
}
}
}
}
+ (void)storePrerenderModuleTasks:(WXModuleMethod *)method forUrl:(NSString *)url
{
WXPrerenderManager *manager = [WXPrerenderManager sharedInstance];
WXPrerenderTask *task = [manager.prerenderTasks objectForKey:[WXPrerenderManager getTaskKeyFromUrl:url]];
if (!task.moduleTasks){
task.moduleTasks = [NSMutableArray new];
}
[task.moduleTasks addObject:method];
if(task){
[manager.prerenderTasks setObject:task forKey:[WXPrerenderManager getTaskKeyFromUrl:url]];
}
}
+ (void)excuteModuleTasksForUrl:(NSString *)url
{
WXPrerenderManager *manager = [WXPrerenderManager sharedInstance];
WXPrerenderTask *task = [manager.prerenderTasks objectForKey:[WXPrerenderManager getTaskKeyFromUrl:url]];
if (task.moduleTasks && [task.moduleTasks count]>0){
for (WXModuleMethod *method in task.moduleTasks) {
[method invoke];
}
}
}
+ (NSString *)getTaskKeyFromUrl:(NSString *)url
{
NSURL *newUrl = [NSURL URLWithString:url];
NSString * newUrlStr = @"";
if(url){
newUrlStr = [NSString stringWithFormat:@"%@/%@",newUrl.host,newUrl.path?:@""];
}
return newUrlStr;
}
+ (void)destroyTask:(NSString *)parentInstanceId
{
WXPrerenderManager *manager = [WXPrerenderManager sharedInstance];
if(!manager.prerenderTasks || [manager.prerenderTasks count] == 0){
return;
}
[manager.prerenderTasks enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
WXPrerenderTask *task = manager.prerenderTasks[key];
if ([task.parentInstanceId isEqualToString:parentInstanceId]) {
[manager removeTask:task];
}
}];
}
- (void)removeTask:(WXPrerenderTask *)task
{
[task.instance destroyInstance];
if(self.prerenderTasks && [self.prerenderTasks count] > 0 && task.url.length > 0){
[self.prerenderTasks removeObjectForKey:[WXPrerenderManager getTaskKeyFromUrl:task.url]];
}
}
@end