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

我们在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 pSQLiteDatabases = null; // 存放连接池中数据库连接的向量
    public static TASQLiteDatabasePool getInstance()
    {

        return mSQLiteDatabasePool;
    }

    public synchronized void createPool()
    {
        // 确保连接池没有创建
        // 如果连接池己经创建了,保存连接的向量 sqLiteDatabases 不会为空
        if (pSQLiteDatabases != null)
        {
            return; // 如果己经创建,则返回
        }
        // 创建保存连接的向量 , 初始时有 0 个元素
        pSQLiteDatabases = new Vector();
        // 根据 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 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 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,这样的目的很明显,就是当一条线程在写数据库时,其他线程只能等待,当写完释放连接后,才能继续访问数据库

你可能感兴趣的:(Android多线程操作sqlite数据库连接池框架的一种设计思路)