SQLite基础及其多线程下使用

SQLite简介

在Android手机数据存储中,一般情况而言有三种:文件存储、SharedPreferences存储、SQLite数据库存储。SQLite是一个嵌入式数据库引擎,针对内存等资源有限的设备(如手机、MP3)提供一种高效的数据库引擎,它的最大存储大小可以高达2TB。

SQLite数据库不像其它数据库,它没有服务器进程。所有的文件都包含在同一个文件中。它的简单的设计是通过在开始一个事务的时候锁定整个数据文件而完成的。

SQLiteDatabase

Android提供了创建和使用SQLite数据库的API。SQLiteDatabase代表了一个数据库对象,对外提供了操作数据库的一些方法,另外还有一个SQLiteOpenHelper工具类提供了一个更简洁的功能。

SQLiteDatabase常用方法

方法名称 方法描述
openOrCreateDatabase(String path, SQLiteDatabase.CursorFactory factory) 打开或创建数据库
insert(String table, String nullColumnHack, ContentValues values) 添加一条记录
delete(String table, String whereClause, String[] whereArgs) 删除一条记录
query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) 查询记录
update(String table, ContentValues values, String whereClause, String[] whereArgs) 修改记录
execSQL(String sql) 执行SQL语句
close() 关闭数据库

openOrCreateDatabase

openOrCreateDatabase方法可以直接使用context.openOrCreateDatabase()或者SQLiteDatabase.openOrCreateDatabase(),
网上查过源码,本以为Context中的openOrCreateDatabase方法是调用的SQLiteDatabase中的,可是查到
public abstract SQLiteDatabase openOrCreateDatabase(String name,int mode, CursorFactory factory);
就止步了,查了相关资料,网上也有人在讨论这些相关东西,仍然没有查到相关源码说明Context是调用SQLiteDatabase中的开启数据库的方法,先把疑问暂且放在这里,或许过些时间就有答案了吧。

ContentValues

在数据库执行insert和update方法的时候我们发现多了一个ContentValues,查看源码发现原来它存放的值都是通过一个HashMap来存储数据的,但是它只能存基本数据类型和String类型的数据。

contentvalues

SQLiteOpenHelper

SQLiteOpenHelper是SQLiteDatabase的一个帮助类,用来管理数据的创建和版本更新。一般是定义一个类继承它,并实现两个抽象方法onCreate (SQLiteDatabase db)和onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)来创建和更新数据库。这个类是在Android开发中最常用的方法。

SQLiteOpenHelper的常用方法

方法名称 方法描述
SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) 构造方法,一般传递一个数据库名称name和一个版本号version
onCreate(SQLiteDatabase db) 创建数据库调用
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 版本更新时调用
getReadableDatabase() 创建或打开一个数据库(磁盘不足只可读,其余情况下可读写)
getWritableDatabase() 创建或打开一个数据库

SQLiteDatabase的简单使用

一般情况下我们都会创建两个类一个是继承了SQLiteOpenHelper的类DBHelper,一个是插查删改操作的管理类DBManager。

DBHelper

DBManager

DBManager是学习Android数据库的关键类,先看一个简单的例子:

然后在你的LogCat中将输出类似下面的日志信息,而你的写数据操作将会无效。

android.database.sqlite.SQLiteException: error code 5:database is locked

上面问题的出现,源于你每创建一个 SQLiteOpenHelper 对象时,实际上也是在新建一个数据库连接。如果你尝试通过多个连接同时对数据库进行写数据操作,其一定会失败。

为确保我们能在多线程中安全地操作数据库,我们需要保证只有一个数据库连接被占用。

我们先编写一个负责管理单个 SQLiteOpenHelper 对象的单例 DatabaseManager 。

这时候又导致下面异常:

java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase

既然我们只有一个数据库连接,Thread1 和 Thread2 对方法Database的调用就会取得一样的 SQLiteDatabase 对象实例,该对象在Dbmanager构造函数时初始化。之后的事情就是,当 Thread1 尝试管理数据库连接时,Thread2 却仍然在使用该数据库连接。这也就是导致 IllegalStateException 崩毁的原因。

因此我们只能在确保数据库没有再被占用的情况下,才去关闭它。在 stackoveflow 上有一些讨论推荐“永不关闭”你的 SQLiteDatabase 。 如果你这样做,你的logcat将会出现以下的信息,因此我不认为这是一个好主意。

Leak foundCaused by: java.lang.IllegalStateException: SQLiteDatabase created and never closed。

接下来是完整的DBManager管理类:

以后每当你需要使用数据库连接,你可以通过调用类 DatabaseManager 的方法openDB()。在方法里面,内置一个标志数据库被打开多少次的计数器。如果计数为1,代表我们需要打开一个新的数据库连接,否则,数据库连接已经存在。

在方法 closeDB() 中,情况也一样。每次我们调用 closeDB() 方法,计数器都会递减,直到计数为0,我们就需要关闭数据库连接了。

接下来就是针对各个不同的业务逻辑新建不同的Dao方法,分别处理在不同的业务逻辑。

SqLite事务管理

讲到数据库操作一般都会涉及到事务,应用程序初始化时需要批量的向sqlite中插入大量数据,单独的使用for+Insert方法导致应用响应缓慢,因为 sqlite插入数据的时候默认一条语句就是一个事务,有多少条数据就有多少次磁盘操作。如果应用初始5000条记录也就是要5000次读写磁盘操作。

使用SQLiteDatabase的beginTransaction()方法可以开启一个事务,程序执行到endTransaction() 方法时会检查事务的标志是否为成功,如果程序执行到endTransaction()之前调用了setTransactionSuccessful() 方法设置事务的标志为成功,则所有从beginTransaction()开始的操作都会被提交,如果没有调用setTransactionSuccessful() 方法则回滚事务

就这样一个简单的插入功能,在模拟器上测试了一下,List的大小为1000,不加事务是输出的数字是72386ms,加上事务以后输出1782ms,一目了然差别也太大了。

结束语

在结语中再说两个点,一个就是CursorAdapter,如果我们的数据都是存储在数据库中的,这个时候我们采用CursorAdapter来适配数据就方面多了,主要涉及到两个方法的使用:

  • void bindView(View view, Context context, Cursor cursor)用于绑定特定View上的数据;
  • View newView(Context context, Cursor cursor, ViewGroup parent)主要用于填充布局用的,就跟BaseAdapter中getView(int position, View convertView, ViewGroup parent)只不过newView仅仅是起到填充布局的作用,填充数据用的是上面的bindView方法。

这种直接加载的方式很容易影响用户体验,数据过多就会出现长时间空白,所以Android还提供了一个LoaderManager异步加载数据库数据,更为详细的示例都在demo代码中,这里也不多说了,有关数据库的操作这次笔记就记录这些,将来遇到新的点再继续记录。

Demo截图及源码下载

sqlite

示例源代码下载

本文地址www.sunnyang.com/244.html

One comment

发表评论