探讨SQLiteDatabase的优雅使用

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

你可能感兴趣的:(数据库,ListView,null,application,database,Class)