不少人在使用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; } }这就是单例加计数器模式,优雅而华丽!