多线程操作SQLite注意事项
- 多线程读写
SQLite实质上是将数据写入一个文件,通常情况下,在应用的包名下面都能找到xxx.db的文件,拥有root权限的手机,可以通过adb shell,看到data/data/packagename/databases/xxx.db这样的文件。
我们可以得知SQLite是文件级别的锁:多个线程可以同时读,但是同时只能有一个线程写。Android提供了SqliteOpenHelper类,加入Java的锁机制以便调用。
如果多线程同时读写(这里的指不同的线程用使用的是不同的Helper实例),后面的就会遇到android.database.sqlite.SQLiteException: database is locked这样的异常。
对于这样的问题,解决的办法就是keep single sqlite connection,保持单个SqliteOpenHelper实例,同时对所有数据库操作的方法添加synchronized关键字。
如下所示:
![](/assets/blank.gif)
![](/assets/blank.gif)
public class DatabaseHelper extends SQLiteOpenHelper {public static final String TAG = "DatabaseHelper";private static final String DB_NAME = "practice.db";private static final int DB_VERSION = 1;private Context mContext;private static DatabaseHelper mInstance;private DatabaseHelper(Context context) {super(context, DB_NAME, null, DB_VERSION);}public synchronized static DatabaseHelper getInstance(Context context) {if (mInstance == null) {mInstance = new DatabaseHelper(context);}return mInstance;}@Overridepublic void onCreate(SQLiteDatabase db) {// TODO Auto-generated method stub }@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// TODO Auto-generated method stub } public synchronized void queryMethod() {SQLiteDatabase readableDatabase = getReadableDatabase();//read operation }public void updateMethod() {SQLiteDatabase writableDatabase = getWritableDatabase();//update operation } }
View Code
Android为我们提供了SqliteOpenHelper类,我们可以通过getWritableDatabase或者getReadableDatabase拿到SQLiteDatabase对象,然后执行相关方法。这2个方法名称容易给人误解,我也在很长的一段时间内想当然的认为getReadabeDatabase就是获取一个只读的数据库,可以获取很多次,多个线程同时读,用完就关闭,实际上getReadableDatabase先以读写方式打开数据库,如果数据库的磁盘空间满了,就会打开失败,当打开失败后会继续尝试以只读方式打开数据库。
![](/assets/blank.gif)
![](/assets/blank.gif)
public synchronized SQLiteDatabase getReadableDatabase() {if (mDatabase != null && mDatabase.isOpen()) {return mDatabase; // The database is already open for business }if (mIsInitializing) {throw new IllegalStateException("getReadableDatabase called recursively");}try {return getWritableDatabase();} catch (SQLiteException e) {if (mName == null) throw e; // Can't open a temp database read-only!Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", e);}SQLiteDatabase db = null;try {mIsInitializing = true;String path = mContext.getDatabasePath(mName).getPath();db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY);if (db.getVersion() != mNewVersion) {throw new SQLiteException("Can't upgrade read-only database from version " +db.getVersion() + " to " + mNewVersion + ": " + path);}onOpen(db);Log.w(TAG, "Opened " + mName + " in read-only mode");mDatabase = db;return mDatabase;} finally {mIsInitializing = false;if (db != null && db != mDatabase) db.close();}}
View Code
在多线程中,如果第一个线程先调用getWritableDatabase,后面线程再次调用,或者第一个线程先调用 getReadableDatabase,后面的线程调用getWritableDatabase,那么后面的这个方法是会失败的,因为数据库文件打开后 会加锁,必须等前面的关闭后后面的调用才能正常执行,正是因为这个原因,可以1 Write+Many Read(有可能产生冲突,因为第一个getReadableDatabase有可能先于getWritableDatabase执行,导致后面的失 败),也可以Many Read,但是不可能Many Write。所以使用单例加上同步的数据库操作方法,就不会出现死锁的问题,这部分例子请参照附件,多线程可以运行的很好,另外关于Sqlite database locking collisions example,网上有很不错的一个例子,可以这里去下载。
其实我觉得理论上可以修改getReadableDatabase方法,打开的数据库都是Read Only的,这样就能同时1 Write+Many Read,只不过要保证打开之前,数据库要创建或者升级好,这样读操作就不会互斥写操作,效率相对更高。
关于数据库关闭的问题,在下面好的习惯中会专门说明。
- 事务
接触过数据库的人,对事务这个概念一定不陌生,它是原子性的,要么执行成功,执行一半失败后会回滚,这样就能保证数据的完整性。SQLiteDatabase也提供了Transaction的相关方法,常见用法:
db.beginTransaction();try {...db.setTransactionSuccessful();} finally {db.endTransaction();}
使用事务对于批量更新有极大的好处,因为单次更新会频繁的调用数据库。
- 除此之外要有几点要注意:
1.关闭Cursor
Cursor如果不关闭,虽然不会导致出错,但是Log中会有错误提示,还是严谨点,Activity中有startManagingCursor的方法,Activity会在生命周期结束时关闭这些Cursor,其他地方,我们则需要用完关闭,以前需要Cursor的Adapter则需要在changeCursor时判断关闭old cursor,在Activity的onDestory方法中关闭cursor。
2.关闭DatabaseHelper
在上述单例Helper例子中,其实一直没有关闭数据库,但是我们阅读getReadabeDatabase和getWritableDatabas的方法,他们会关闭Old SQLiteDatabase的,我们只需要在Application的onTerminal方法中关闭即可,这样也能避免多线程中,一个线程关闭了数据库,导致其他线程使用的时候失败的问题。
实质上,数据库是一个文件引用,单例模式下,不关闭也不会出现问题,让它保持随单例的生命周期关闭就好了。
3.在循环外面获取ColumnIndex,如果表中列不是很多,每次查询又返回所有列的话,可以将列的index定义到TABLE_COLUMNS中去,这样每次获取指定列数据的话,就不用去查找index了。
4.数据库存放的数据类型
Android提供了多种数据存储的方法,文件,数据库,SharePreference,网络等,要根据情况选择合适的方式,不要把什么东西都往数据库中塞。
下面的几种情况就不适合放到数据库中:
1)图片等二进制数据:如果是图片的话,可以将文件名称或者路径保存到数据库中,真正的文件可以作为缓存文件保存在文件系统中。
2)临时数据:定位获取到的Location,登录的Session等。
3)日志数据:可以写入文件中,通常是log_xxxx.txt。
转载于:https://www.cnblogs.com/kkong/p/3555282.html
多线程操作SQLite注意事项相关推荐
- FMDatabaseQueue 数据库多线程操作、事务处理
SQLite数据库多线程操作: 在上面一节中已经讲过FMDB的用法了,接下来讲讲sqlite在都线程中的用法.如果应用中使用了多线程操作数据库,那么就需要使用FMDatabaseQueue来保证线程安 ...
- MFC不能多线程操作控件的原因
对于大多数mfc对象,请不要在线程间传递它们,不管是栈上的还是堆上的!原因如下: mfc的大多数类不是线程安全的,调用传入对象的成员函数可能不会报错,但是未必能达到程序预定的功能! mfc与界 ...
- java多线程 注意事项_多线程使用及注意事项
1.并行和并发的区别 2.多线程(高并发编程)的优点 3.多线程程序需要注意事项 4.线程的启动与安全中止 5.怎么安全中止线程(interrupt()) 并行和并发的区别: 一个是同时执行,一个是交 ...
- Qt中操作SQLite数据库
0.前言 SQLite是一款开源.轻量级.跨平台的数据库,无需server,无需安装和管理配置.它的设计目标是嵌入式的,所以很适合小型应用,也是Qt应用开发种常用的一种数据库. 1.驱动 Qt SQL ...
- android查询mysql并显示_Android操作SQLite数据库(增、删、改、查、分页等)及ListView显示数据的方法详解...
本文实例讲述了Android操作SQLite数据库(增.删.改.查.分页等)及ListView显示数据的方法.分享给大家供大家参考,具体如下: 由于刚接触android开发,故此想把学到的基础知识记录 ...
- Poco库使用:操作SQLite数据库
Sqlite是在工程项目中使用比较多的小型数据库.由于其非常小巧且占用资源低,深受开发者喜欢.这里就介绍一下如何通过Poco框架操作SQLite数据库.这里假设你已经完整编译了Poco库,如果还没有编 ...
- 一行 Python 实现并行化 -- 日常多线程操作的新思路 - 左手键盘,右手书 - SegmentFault...
一行 Python 实现并行化 -- 日常多线程操作的新思路 - 左手键盘,右手书 - SegmentFault
- android 开发使用adb操作sqlite
android 开发使用adb操作sqlite. 1.启动模拟器或连接android实体机 2.在命令行输入:adb shell 3.进入目录:data/data/应用程序包名/databases/ ...
- python sqlalchemy操作SQLite
日期转时间: from sqlalchemy import Column, Integer, String, Float, Date date = Column(Date) data="20 ...
最新文章
- 尚硅谷学习笔记-节点的常用属性和方法
- LINUX相关的镜像源网站大全,个人收集完整版!
- [原创]linux简单之美(二)
- I/0口输入输出实验 流水灯程序 P0、P1、P2、P3口作为输出口,连接八只发光二极管,编写程序,使发光二极管从左至右循环点亮。
- 2021零售行业营销自由白皮书
- Mac下iTerm2的ls输出如何显示文件件颜色呢?
- Python调用安卓手机相机接口进行拍照
- mysql load data infile 上传数据 不显示_第22问:我有带外键的表,你有数据么?
- 三种文本特征提取(TF-IDF/Word2Vec/CountVectorizer)
- Qt+VS2019+OpenCV 使用问题 - Cound not find “QT“
- 战胜25名医生:AI真能成为医疗界的“擂主”?
- WCF+SQL Server 2008 明源售楼系统项目解析
- 凸集(Convex sets)
- The server encountered an internal error () that prevented it from fulfilling this request
- SpringBoot 2.3 新特性之优雅停机
- 哪些seo搜索技巧你不会用?
- 我的物联网项目(二十九) 线上前期运营
- 《你好,安怡》热播,AI觉醒,奇点临近?
- [解决网络出现“正在连接”的问题]
- H3C WX2510h无线控制器如何网关式部署无线网络
热门文章
- python 中空NULL的表示
- e1载波把32个信道按_什么是Wi-Fi 6?这12个问题为你讲清楚
- 推荐系统思维导图——第二章
- 算!力!羊!毛!5000核时计算资源终于开放使用了!
- 昔年浅谈电商服务业务应如何推广
- kafka数据 落盘_终于知道Kafka为什么这么快了!
- php视图最佳实践,PHP 的最佳实践
- 使用@onetomany取不到值_SpringCloud微服务架构篇2:微服务基础—SpringBoot使用
- 图片居中裁剪_魔镜,魔镜,谁最美丽!利用PS图层混合模式打造图片幻觉效果
- python3 常见命令