探讨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;
    }
}
这就是单例加计数器模式,优雅而华丽!

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值