Android多线程操作sqlite数据库连接池框架的一种设计思路

来源:互联网 发布:淘宝卡dnf称号 编辑:程序博客网 时间:2024/04/30 08:58

我们在Android应用程序开发当中经常会用到数据库,一般在有两种主要开发思路。
第一种:每次需要对数据库操作的时候建立连接,操作完成后,马上关闭连接。
这个方式一般用于不怎么频繁操作数据库的情况,用完后马上关闭达到节省系统资源的目的。
第二种:应用程序一启动或在第一次使用数据库建立连接后,就始终保持数据库的连接而不关闭,知道退出该应用程序 。
这个方式一般常用频繁操作数据库,因为频繁的打开关闭数据库会影响程序的性能。

我们这博客主要是介绍第二种设计思路,即Sqlite数据库连接池的设计方案。
1》继承SQLiteOpenHelper类,实现它的相关重写方法,伪代码如下

public class XxxHelper extends SQLiteOpenHelperpublic void onCreate(SQLiteDatabase db)    {    }    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)    {    } }

2》定义一个封装类,用于维护SQLiteDatabase对象和其对数据库的所有操作(增,删,改,查等),伪代码如下:

public class XxxSQLiteDatabase{        private SQLiteDatabase mSQLiteDatabase = null;    private XxxHelper mDatabaseHelper = null;    public SQLiteDatabase openDatabase(){        mSQLiteDatabase = mDatabaseHelper.getReadableDatabase();        return mSQLiteDatabase;    }    public void insert(){}    public void query(){}    public void updat(){}    public void delete(){}}

3》定义数据库连接池类,也是最关键的一个类,代码如下:

public class XxxSQLiteDatabasePool{    private int initialSQLiteDatabase = 2; // 连接池的初始大小    private int incrementalSQLiteDatabase = 2;// 连接池自动增加的大小    private int maxSQLiteDatabase = 10; // 连接池最大的大小    private Vector<PooledSQLiteDatabase> pSQLiteDatabases = null; // 存放连接池中数据库连接的向量    public static TASQLiteDatabasePool getInstance()    {        return mSQLiteDatabasePool;    }    public synchronized void createPool()    {        // 确保连接池没有创建        // 如果连接池己经创建了,保存连接的向量 sqLiteDatabases 不会为空        if (pSQLiteDatabases != null)        {            return; // 如果己经创建,则返回        }        // 创建保存连接的向量 , 初始时有 0 个元素        pSQLiteDatabases = new Vector<PooledSQLiteDatabase>();        // 根据 initialConnections 中设置的值,创建连接。        createSQLiteDatabase(this.initialSQLiteDatabase);    }    private void createSQLiteDatabase(int numSQLiteDatabase)    {        // 循环创建指定数目的数据库连接        for (int x = 0; x < numSQLiteDatabase; x++)        {            // 是否连接池中的数据库连接的数量己经达到最大?最大值由类成员 maxSQLiteDatabase            // 指出,如果 maxSQLiteDatabase 为 0 或负数,表示连接数量没有限制。            // 如果连接数己经达到最大,即退出。            if (this.maxSQLiteDatabase > 0                    && this.pSQLiteDatabases.size() >= this.maxSQLiteDatabase)            {                break;            }            try            {                // 增加一个XxxSQLiteDatabase到连接池中                pSQLiteDatabases.addElement(new PooledSQLiteDatabase(                        newSQLiteDatabase()));            } catch (Exception e)            {            }        }    }    /**     * 创建一个新的数据库连接并返回它     *      * @return 返回一个新创建的数据库连接     */    private XxxSQLiteDatabase newSQLiteDatabase()    {        // 创建一个数据库连接        XxxSQLiteDatabase sqliteDatabase = new XxxSQLiteDatabase(context, params);        sqliteDatabase.openDatabase();        return sqliteDatabase; // 返回创建的新的数据库连接    }    /**     * 通过调用 getFreeSQLiteDatabase() 函数返回一个可用的数据库连接 ,     * 如果当前没有可用的数据库连接,并且更多的数据库连接不能创 建(如连接池大小的限制),此函数等待一会再尝试获取。     *      * @return 返回一个可用的数据库连接对象     */    public synchronized XxxSQLiteDatabase getSQLiteDatabase()    {        // 确保连接池己被创建        if (pSQLiteDatabases == null)        {            return null; // 连接池还没创建,则返回 null        }        XxxSQLiteDatabase sqliteDatabase = getFreeSQLiteDatabase(); // 获得一个可用的数据库连接        // 如果目前没有可以使用的连接,即所有的连接都在使用中        while (sqliteDatabase == null)        {            // 等一会再试            wait(250);            sqliteDatabase = getFreeSQLiteDatabase(); // 重新再试,直到获得可用的连接,如果            // getFreeConnection() 返回的为 null            // 则表明创建一批连接后也不可获得可用连接        }        return sqliteDatabase;// 返回获得的可用的连接    }    /**     * 本函数从连接池向量 pSQLiteDatabases 中返回一个可用的的数据库连接,如果 当前没有可用的数据库连接,本函数则根据     * incrementalSQLiteDatabase 设置 的值创建几个数据库连接,并放入连接池中。 如果创建后,所有的连接仍都在使用中,则返回     * null     *      * @return 返回一个可用的数据库连接     */    private XxxSQLiteDatabase getFreeSQLiteDatabase()    {        // 从连接池中获得一个可用的数据库连接        XxxSQLiteDatabase sqLiteDatabase = findFreeSQLiteDatabase();        if (sqLiteDatabase == null)        {            // 如果目前连接池中没有可用的连接            // 创建一些连接            createSQLiteDatabase(incrementalSQLiteDatabase);            // 重新从池中查找是否有可用连接            sqLiteDatabase = findFreeSQLiteDatabase();            if (sqLiteDatabase == null)            {                // 如果创建连接后仍获得不到可用的连接,则返回 null                return null;            }        }        return sqLiteDatabase;    }    /**     * 查找连接池中所有的连接,查找一个可用的数据库连接, 如果没有可用的连接,返回 null     *      * @return 返回一个可用的数据库连接     */    private XxxSQLiteDatabase findFreeSQLiteDatabase()    {        XxxSQLiteDatabase sqliteDatabase = null;        PooledSQLiteDatabase pSQLiteDatabase = null;        // 获得连接池向量中所有的对象        Enumeration<PooledSQLiteDatabase> enumerate = pSQLiteDatabases                .elements();        // 遍历所有的对象,看是否有可用的连接        while (enumerate.hasMoreElements())        {            pSQLiteDatabase = (PooledSQLiteDatabase) enumerate.nextElement();            if (!pSQLiteDatabase.isBusy())            {                // 如果此对象不忙,则获得它的数据库连接并把它设为忙                sqliteDatabase = pSQLiteDatabase.getSqliteDatabase();                pSQLiteDatabase.setBusy(true);                // 测试此连接是否可用                if (!testSQLiteDatabase(sqliteDatabase))                {                    // 如果此连接不可再用了,则创建一个新的连接, // 并替换此不可用的连接对象,如果创建失败,返回 null                    sqliteDatabase = newSQLiteDatabase();                    pSQLiteDatabase.setSqliteDatabase(sqliteDatabase);                }                break;                // 己经找到一个可用的连接,退出            }        }        return sqliteDatabase;// 返回找到到的可用连接    }}   /**     * 此函数返回一个数据库连接到连接池中,并把此连接置为空闲。 所有使用连接池获得的数据库连接均应在不使用此连接时返回它。     *      * @param 需返回到连接池中的连接对象     */    public void releaseSQLiteDatabase(TASQLiteDatabase sqLiteDatabase)    {        // 确保连接池存在,如果连接没有创建(不存在),直接返回        if (pSQLiteDatabases == null)        {            return;        }        PooledSQLiteDatabase pSqLiteDatabase = null;        Enumeration<PooledSQLiteDatabase> enumerate = pSQLiteDatabases                .elements();        // 遍历连接池中的所有连接,找到这个要返回的连接对象        while (enumerate.hasMoreElements())        {            pSqLiteDatabase = (PooledSQLiteDatabase) enumerate.nextElement();            // 先找到连接池中的要返回的连接对象            if (sqLiteDatabase == pSqLiteDatabase.getSqliteDatabase())            {                // 找到了 , 设置此连接为空闲状态                pSqLiteDatabase.setBusy(false);                break;            }        }    }    /**     * 内部使用的用于保存连接池中连接对象的类 此类中有两个成员,一个是数据库的连接,另一个是指示此连接是否 正在使用的标志。     */    class PooledSQLiteDatabase    {        XxxSQLiteDatabase sqliteDatabase = null;// 数据库连接        boolean busy = false; // 此连接是否正在使用的标志,默认没有正在使用        // 构造函数,根据一个 XxxSQLiteDatabase 构告一个 PooledSQLiteDatabase 对象        public PooledSQLiteDatabase(XxxSQLiteDatabase sqliteDatabase)        {            this.sqliteDatabase = sqliteDatabase;        }        // 返回此对象中的连接        public XxxSQLiteDatabase getSqliteDatabase()        {            return sqliteDatabase;        }        // 设置此对象的,连接        public void setSqliteDatabase(XxxSQLiteDatabase sqliteDatabase)        {            this.sqliteDatabase = sqliteDatabase;        }        // 获得对象连接是否忙        public boolean isBusy()        {            return busy;        }        // 设置对象的连接正在忙        public void setBusy(boolean busy)        {            this.busy = busy;        }    }}

为了解释下上面代码的思路,我们先来看看我们如何去调用这个数据库连接池的。

        XxxSQLiteDatabasePool pool = TASQLiteDatabasePool.getInstance();        pool.createPool();        XxxSQLiteDatabase sqliteDatabase = pool.getSQLiteDatabase();        sqliteDatabase.insert();        pool.releaseSQLiteDatabase(sqliteDatabase);

1.连接池的结构,连接池主要由一个线程安全的vector集合实现,并指定该集合的最大连接数,也就是代码中的maxSQLiteDatabase成员变量。在调用createPool方法时,会初始化这个vecotr集合,并默认创建任意条连接。

2.获取数据库对象的过程,调用getSQLiteDatabase()方法去vector集合中获取数据库对象,如果当前集合中有未锁定的数据库对象,那简单了,直接取出使用就行,并给该数据库对象加锁,以防止其他线程使用该连接对象。如果当前集合中的数据库对象都已锁定的话,首先判断集合中的对象有没有超过最大连接数 ,没超过则重新生成一个连接对象,超过最大连接数的话,就线程等待,直到其他线程释放数据库连接对象。
至此,设计的思路完成

扩展话题:
Sqlite3数据库目前之支持一个线程同一时间对它的写,读可以多线程同时进行。这样就会引发一个问题,如果出现2条以上的线程对数据库的同时写操作,系统就会抛出异常 “database is locked“。

碰到这种问题,怎么解决呢?
目前我所知道的方式只能做到规避不能彻底解决这个问题。如果你的应用程序只是对数据库的读操作频繁,写不是太频繁,这个你可以将最大连接数设置大于1。
如果你的程序存在多线程频繁的写数据库的操作时,将最大连接书设置1,这样的目的很明显,就是当一条线程在写数据库时,其他线程只能等待,当写完释放连接后,才能继续访问数据库

0 0
原创粉丝点击