探究Android SQLite3多线程
最近做项目时在多线程读写数据库时抛出了异常,这自然是我对SQlite3有理解不到位的地方,所以事后仔细探究了一番。
关于getWriteableDataBase()和getReadableDatabase()的真正作用
getWriteableDataBase()其实是相当于getReadableDatabase()的一个子方法,getWriteableDataBase()是只能返回一个以读写方式打开的SQLiteDatabase的引用,如果此时数据库不可写时就会抛出异常,比如数据库的磁盘空间满了的情况。而getReadableDatabase()一般默认是调用getWriteableDataBase()方法,如果数据库不可写时就会返回一个以只读方式打开的SQLiteDatabase的引用,这就是二者最明显的区别。
关键源码如下:public synchronized SQLiteDatabase getWritableDatabase() {
if (mDatabase != null) {
if (!mDatabase.isOpen()) {
// darn! the user closed the database by calling mDatabase.close()
mDatabase = null;
} else if (!mDatabase.isReadOnly()) {
return mDatabase; // The database is already open for business
}
}
... ... public synchronized SQLiteDatabase getReadableDatabase() {
if (mDatabase != null) {
if (!mDatabase.isOpen()) {
// darn! the user closed the database by calling mDatabase.close()
mDatabase = null;
} else {
return mDatabase; // The database is already open for business
}
}
... ...
try {
return getWritableDatabase();
}
... ...
SQLiteDatabase的同步锁
其实在只使用一个SQLiteDatabase的引用时,SQLiteDatabase对CRUD操作都会加上一个锁(因为是db文件,所以精确至数据库级),这就保证了在同一时间你只能进行一项操作,无论是不是在同一个线程中,这就导致了如果你在程序中对SQLiteOpenHelper使用了单例模式,那么你对数据库读写进行任何的优化操作都是"徒劳"。多线程读数据库
仔细看源码你会发现,在数据库操作中只有add,delete,update会调用lock(),而query()是不会调用的,但是在加载数据时,调用了SQLiteQuery的fillWindow方法,而该方法依然会调用SQLiteDatabase.lock(),所以要想真正的实现多线程读数据库,只能每个线程使用各自的SQLiteOpenHelper对象进行读操作,这样就可避开同步锁。关键源码如下:/* package */ int fillWindow(CursorWindow window,
int maxRead, int lastPos) {
long timeStart = SystemClock.uptimeMillis();
mDatabase.lock();
mDatabase.logTimeStat(mSql, timeStart, SQLiteDatabase.GET_LOCK_LOG_PREFIX);
try {... ...
多线程读写
实现多线程读写的关键是enableWriteAheadLogging属性,这个方法 API Level 11添加的,也就是所3.0以上的版本就基本不可能实现真正的多线程读写了。简单的说通过调用enableWriteAheadLogging()和disableWriteAheadLogging()可以控制该数据是否被运行多线程读写,如果允许,它将允许一个写线程与多个读线程同时在一个SQLiteDatabase上起作用。实现原理是写操作其实是在一个单独的log文件,读操作读的是原数据文件,是写操作开始之前的内容,从而互不影响。当写操作结束后读操作将察觉到新数据库的状态。当然这样做的弊端是将消耗更多的内存空间。多线程写
这个就不用多想了,SQLite压根不支持,如果实在有需求可以使用多个数据库文件。备注
(1)你有没有想SQLite最多支持多少个数据库连接,其实在官方API文档(enableWriteAheadLogging ()方法)中给出了最精确的答案:The maximum number of connections used to execute queries in parallel is dependent upon the device memory and possibly other properties.就是看你有多少内存,但是我感觉这话说的有点大,是不?哈哈。(2)当你在多线程中只使用一个SQLiteDatabase的引用时,需要格外注意你SQLiteDataBase.close()调用的时机,因为你是使用的同一个引用,比如在一个线程中当一个Add操作结束后立刻关闭了数据库连接,而另一个现场中正准备执行查询操作,但此时db已经被关闭了,然后就会报异常错误。此时一般有三种解决方案,①简单粗暴给所有的CRUD添加一个 synchronized关键字;②永远不关闭数据库连接,只在最后退出是关闭连接。其实每次执行getWriteableDataBase()或getReadableDatabase()方法时,如果有已经建立的数据库连接则直接返回(例外:如果旧的连接是以只读方式打开的,则会在新建连接成功的前提下,关闭旧连接),所以程序中将始终保持有且只有一个数据库连接(前提是单例),资源消耗的很少。③可以自己进行引用计数,简单示例代码如下:
//打开数据库方法
public synchronized SQLiteDatabase openDatabase() {
if (mOpenCounter.incrementAndGet() == 1) {
// Opening new database
try {
mDatabase = sInstance.getWritableDatabase();
} catch (Exception e) {
mDatabase = sInstance.getReadableDatabase();
}
}
return mDatabase;
} //关闭数据库方法
public synchronized void closeDatabase() {
if (mOpenCounter.decrementAndGet() == 0) {
// Closing database
mDatabase.close();
}
}
(3)还有一些比较好的习惯和常识,例如关闭Cursor,使用Transaction,SQLite存储数据时其实不区分类型,以及SQLite支持大部分标准SQL语句,增删改查语句都是通用的等等。
探究Android SQLite3多线程的更多相关文章
- Android网络多线程断点续传下载
本示例介绍在Android平台下通过HTTP协议实现断点续传下载. 我们编写的是Andorid的HTTP协议多线程断点下载应用程序.直接使用单线程下载HTTP文件对我们来说是一件非常简单的事.那么,多 ...
- 教你如何在 Android 使用多线程下载文件
# 教你如何在 Android 使用多线程下载文件 前言 在 Android 日常开发中,我们会经常遇到下载文件需求,这里我们也可以用系统自带的 api DownloadManager 来解决这个问题 ...
- Android进阶——多线程系列之Thread、Runnable、Callable、Future、FutureTask
多线程一直是初学者最抵触的东西,如果你想进阶的话,那必须闯过这道难关,特别是多线程中Thread.Runnable.Callable.Future.FutureTask这几个类往往是初学者容易搞混的. ...
- android开发--多线程
android中的几种多线程实现方式: 1)Activity.runOnUiThread(Runnable) 2)View.post(Runnable) ;View.postDelay(Runnabl ...
- Android handle 多线程练习
Android handle <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android&quo ...
- Android SQLite3工具常用命令行总结
Android SDK的tools目录下提供了一个sqlite3.exe工具,这是一个简单的sqlite数据库管理工具.开发者可以方便的使用其对sqlite数据库进行命令行的操作. 程序运行生成的*. ...
- (原创)android Sqlite多线程访问异常解决方案
在开发Android的程序的时候sqlite数据库是经常用到的:在多线程访问数据库的时候会出现这样的异常:java.lang.IllegalStateException: Cannot perform ...
- 36.Android之多线程和handle更新UI学习
android经常用到多线程更新UI,今天学习下. 首先布局比较简单: <?xml version="1.0" encoding="utf-8"?> ...
- Android之多线程断点下载
本文主要包含多线程下载的一些简单demo,包括三部分 java实现 android实现 XUtils开源库实现 注意下载添加网络权限与SD卡读写权限 java实现多线程下载 public class ...
随机推荐
- 第一个ios程序
1.ios的理解: Operating System,简称OS,操作系统,ios是苹果操作系统. 2.Xcode开发环境: 苹果公司开发的编程软件,是开发人员建立OS X 和 iOS 应用程序的最快捷 ...
- Java---注解、类加载器-加强-实现运行任意目录下class中加了@MyTest的空参方法
做自己的类加载器 虚拟机的核心是通过类加载器来加载.class文件,然后进行相应的解析执行.那么我们可以自己做类加载器,手动加载需要的.class以进行解析执行,从而扩展虚拟机的功能. 以下内容摘自A ...
- HDOJ 2026 首字母变大写
Problem Description 输入一个英文句子,将每个单词的第一个字母改成大写字母. Input 输入数据包含多个测试实例,每个测试实例是一个长度不超过100的英文句子,占一行. Outpu ...
- openSession和getCurrentSession的比较
在比较openSession和getCurrentSession这两个方法之前,我们先认识一下这两个方法. 在进行配置信息管理时,我们一般进行一下简单步骤: Configuration cfg = n ...
- 《University Calculus》-chaper13-多重积分-二重积分的引入
这一章节我们开始对多重积分的研究. 在此之前,我们首先来回忆起积分的过程,在平面中,面临求解不规则图形的面积(常叫曲边梯形)的时候,我们可以采取建立直角坐标系,然后通过得到不规则图形边界的函数表达式f ...
- angularjs简单实现$http.post(CORS)跨域及$http.post传参方式模拟jQuery.post
1.开启angularjs的CORS支持 .config(function($httpProvider) { // CORS post跨域配置 $httpProvider.defaults.useXD ...
- Away3D 4.1.4 中实现骨骼绑定
骨骼的绑定归根结底就是将目标骨骼的位置以及旋转数据,同步给要绑定的显示对象. 先来看BindingTag.as package away3d.entities { import away3d. ...
- struts2原理架构图
struts2 原理架构图
- Protobuf的自动反射消息类型的方法
1. 每个消息头部中带上type name,作为消息的类型标识 2. 通过type name可以找到描述符Descriptor*, FindMessageTypeByName 3. 通过描述符Desc ...
- Linux字符串函数集
//Linux字符串函数集: 头文件:string.h 函数名: strstr 函数原型:extern char *strstr(char *str1, char *str2); 功能:找出str2字 ...