不少人在使用SQLiteDatabase数据库时都很纠结,到底如何使用才算优雅?本文就此问题进行探讨。
使用SQLiteDatabase,不外乎下面几种方式:
1. 在每个CRUD方法开始时调用getWritableDatabase()取得数据库,方法结束时close db
2. 在每个activity、service等生命周期开始时new一个新db对象,取得数据库连接;然后在destroy时close db
3. 在Application定义一个全局的数据库对象
4. 单例模式
先看第一种。缺点一大堆:代码臃肿;每次查询都要打开关闭数据库,性能方面也受到影响;最致命的一点是,有时候查询需要返回一个Cursor对象到其他地方使用,比如ListView,但是db又在查询方法结束时close了,导致运行直接抛异常。所以这种方式最好不用。
第二种方式和第一种类似,只是把打开关闭db的位置挪了一下,使db对象的生存范围扩大了一点。如果在onDestroy()中关闭,又会带来另外一个问题:打开新activity时,老activity只是被隐藏了,还在,导致打开的db越来越多,显然不可取。不过这种方式可以改进一下,改在onResume()中生成db,onPause()中关闭db,这样能保证系统中不会有多余的db实例,但仍不是很优雅。
第三种方式保证系统在运行中仅持有一个db实例,但是问题来了,在哪里close db呢?onTerminate()?不行,系统开始运行时会执行application的onCreate(),但是退出时不会执行onTerminate(),不信的话可以试一下。
最后讨论一下本文的重头戏,单例。
普通的单例模式,在我看来,和第三种application模式本质上是相同的,没区别,当然也就存在application方式的问题。而且,还会有其他问题。在创建SQLiteOpenHelper时需要传递一个Context进去,假如Activity A创建了这个db,然后其他Activity也在使用这个单例对象,而Activity A又需要被销毁,但是由于单例对象持有这个Context,导致Activity A不能被有效销毁。
怎么办?
完美解决
还是使用单例,不过需要改进一下。
首先,针对close问题,我们设一个计数器,在activity的onCreate()方法get单例实例,计数器加1,在onDestroy()方法释放,计数器减1,当计数器为0时,close db。
其次,创建SQLiteOpenHelper不使用activity的context,改用application的context。
DbAdaptor代码如下:
public class DbAdaptor {
private static final String DATABASE_CREATE =
"create table users (_id integer primary key autoincrement, " +
"account text not null, password text not null);";
private static final String DATABASE_NAME = "test";
private static final int DATABASE_VERSION = 2;
private static int count;
private static DbAdaptor dbAdaptor = null;
private SQLiteDatabase mDb;
class DbHelper extends SQLiteOpenHelper{
public DbHelper(Context context) {
super(context.getApplicationContext(), DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS users");
onCreate(db);
}
}
private DbAdaptor(Context ctx){
DbHelper help = new DbHelper(ctx);
mDb = help.getWritableDatabase();
}
public static DbAdaptor getInstance(Context ctx){
if(null == dbAdaptor){
dbAdaptor = new DbAdaptor(ctx);
}
count++;
return dbAdaptor;
}
public void release(){
count--;
if(count == 0){
mDb.close();
dbAdaptor = null;
}
}
}
注意一定要使用application的context;release时不仅仅要close db,还要将dbAdapter设为null。
每个需要使用数据库的activity都要在onCreate()中调用getInstance(),在onDestroy()中调用release(),注意保证这两个方法都只调用一次,否则count计数器不能正确增减。这些代码没必要在每个activity都写一遍,定义一个基类就可以了,子activity通过调用getDataBase()来使用db,代码如下:
public class BaseActivity extends Activity{
private DbAdaptor db;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
db = DbAdaptor.getInstance(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
db.release();
}
public DbAdaptor getDataBase(){
return db;
}
}
这就是单例加计数器模式,优雅而华丽!