改造SQLitePlugin插件,用FMDB统一管理
来源:互联网 发布:空巢老人最新数据 编辑:程序博客网 时间:2024/06/08 01:47
接上篇博客:
hybrid应用的database locked问题
我们的是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
options就是js传过来的参数,里面有sql,params,query,qid这4个key,所以接下来我们调用FMDB API所需要的参数,也是从其中取出来。而这个方法最后的返回值,必须包含qid,type,result,error,具体来说,qid是原样返回,type可能是success或error,result则必须包含rows,也就是select查询的结果(其实还有rowAffected和其他一些key,但是检查了发现没有用,所以新的实现就忽略了这些key)
上述的参数和返回值搞清楚以后,剩下的部分就是重新实现。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];}
只要熟悉FMDB的API,上面的代码都是很直接的,没有什么需要特别说明的。
另外,这个plugin持有一个dbHelper的实例变量:
{ YLSDatabaseHelper *dbHelper;}-(CDVPlugin*) initWithWebView:(UIWebView*)theWebView{ self = (SQLitePlugin*)[super initWithWebView:theWebView]; if (self) { dbHelper = [YLSDatabaseHelper sharedInstance]; } return self;}
这个YLSDatabaseHelper是个单例,原生的代码以及这个插件,都通过这个唯一入口访问数据库,因此保证了不会并发写(FMDatabaseQueue的内部机制)。如果这个dbHelper不是单例,而是有多个实例,其实是无法避免database locked的
@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
这样改造之后,对js部分完全没有影响,WEB里的业务代码一点都不需要修改。但是毕竟是改造,其实如果在首版本就能意识到这个问题,可以直接这样写一个插件,不需要引入SQLitePlugin,然后插件的js部分也可以简单很多
- 改造SQLitePlugin插件,用FMDB统一管理
- vim 插件改造
- jquery select2插件 改造
- 用slf4j统一管理日志总结
- 用FMDB 还是 CoreData
- FMDB
- FMDB
- FMDB
- FMDB
- FMDB
- fmdb
- FMDB
- FMDB
- fmdb
- FMDB
- FMDB
- fmdb
- FMDB
- 3.QT中QCommandLineParser和QCommandLineOption解析命令行参数
- add "remote-notification" to the list of your supported UIBackgroundModes in your Info.plist.
- 安装软件错误处理方法集合
- mysql 完美卸载
- leetcode Add Two Numbers VS 2010
- 改造SQLitePlugin插件,用FMDB统一管理
- 计蒜客011-移除数组中的重复元素
- 修改UISearchBar风格为iOS7之后风格
- 【C】【CrytoAPI】【加密解密】基于CryptoAPI的文件加解密系统设计与实现
- android Unable to execute dex: Multiple dex files define 解决方法
- jsp页面自动调用action方法
- hoj 1744 Grandpa is Famoust
- Linux文件系统十问---深入理解文件存储方式
- android BroadcastReceiver