接上篇博客:
我们的是hybrid应用,基于cordova。做第一个版本的时候没经验,原生的部分用FMDB访问数据库,WEB的部分就直接找了一个SQLitePlugin用。早期的时候,原生代码和js访问数据库都是错开的,所以没有发生问题。但是到了现在,有些场景需要2边一起访问数据库,由于sqlite不支持并发写,就产生了database locked问题
其实回头看,如果在首版本就基于FMDB自己写一个cordova插件提供给js调用是很容易的,但是到了现在再改造就麻烦了很多,因为业务代码已经大量调用了SQLitePlugin的js接口,所以SQLitePlugin的js部分肯定是不能动了,不然业务代码就都得改。所以最后决定采用的方案,就是把SQLitePlugin的原生部分,重新借助FMDB实现一次。这样应用的原生代码,和cordova插件,都是用FMDatabaseQueue来管理,就不会发生锁库的现象了
改造的过程难度不大,主要就是2大块:
1、把SQLitePlugin里操作数据库的代码,比如sqlite3_step之类的,用FMDB提供的API替换掉
2、通过DEBUG,弄清楚js部分和原生代码部分的参数和返回值,然后适配接口,保证正确处理js传来的参数,并返回和原来的实现一样的返回值
把所有代码读了一遍,发现其实核心部分是这个方法:
-(CDVPluginResult*) executeSqlWithDict: (NSMutableDictionary*)options
上述的参数和返回值搞清楚以后,剩下的部分就是重新实现。SQLitePlugin原来的代码有1000行左右,所做的也无外乎调用sqlite3的api,还有处理类型转换,参数绑定等常规动作,这些事情在FMDB里都已经做过了,所以借助FMDB重新实现是非常简单的。改造后的代码只有200行不到,核心也是这个方法:
-(CDVPluginResult*) executeSqlWithDict: (NSMutableDictionary*)options
{
NSString *sql = [options objectForKey:@"sql"];
NSArray *params = [options objectForKey:@"params"];
NSMutableArray *resultRows = [NSMutableArray array];// 查询结果集
__block NSDictionary *error = nil;
[dbHelper doOperation:^(FMDatabase *db){
if([[sql lowercaseString] hasPrefix:@"select"]){
FMResultSet *rs = [db executeQuery:sql withArgumentsInArray:params];
if(!rs){
error = @{@"code":[NSNumber numberWithInt:[db lastErrorCode]], @"message": [db lastErrorMessage]};
}else{
while([rs next]){
[resultRows addObject:[rs resultDictionary]];
}
[rs close];
}
}else{
BOOL result = [db executeUpdate:sql withArgumentsInArray:params];
if(!result){
error = @{@"code":[NSNumber numberWithInt:[db lastErrorCode]], @"message": [db lastErrorMessage]};
}
}
}];
if(error){
return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:error];
}
NSDictionary *cdvResult = @{@"rows": resultRows};
return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:cdvResult];
}另外,这个plugin持有一个dbHelper的实例变量:
{
YLSDatabaseHelper *dbHelper;
}
-(CDVPlugin*) initWithWebView:(UIWebView*)theWebView
{
self = (SQLitePlugin*)[super initWithWebView:theWebView];
if (self) {
dbHelper = [YLSDatabaseHelper sharedInstance];
}
return self;
}@implementation YLSDatabaseHelper
{
FMDatabaseQueue* queue;
}
-(id) init
{
self = [super init];
if(self){
NSString *dbFilePath = [YLSGlobalUtils getDatabaseFilePath];
queue = [FMDatabaseQueue databaseQueueWithPath:dbFilePath];
}
return self;
}
+(YLSDatabaseHelper*) sharedInstance
{
static dispatch_once_t pred = 0;
__strong static id _sharedObject = nil;
dispatch_once(&pred, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
+(void) refreshDatabaseFile
{
YLSDatabaseHelper *instance = [self sharedInstance];
[instance doRefresh];
}
-(void) doRefresh
{
NSString *dbFilePath = [YLSGlobalUtils getDatabaseFilePath];
queue = [FMDatabaseQueue databaseQueueWithPath:dbFilePath];
}
-(void) doOperation:(void(^)(FMDatabase*))block
{
[queue inDatabase:^(FMDatabase *db){
block(db);
}];
}
@end原文:http://blog.csdn.net/kyfxbl/article/details/41986541