您的位置:首页 > 移动开发 > Android开发

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

2017-03-03 23:57 579 查看
我们在Android应用程序开发当中经常会用到数据库,一般在有两种主要开发思路。

第一种:每次需要对数据库操作的时候建立连接,操作完成后,马上关闭连接。

这个方式一般用于不怎么频繁操作数据库的情况,用完后马上关闭达到节省系统资源的目的。

第二种:应用程序一启动或在第一次使用数据库建立连接后,就始终保持数据库的连接而不关闭,知道退出该应用程序 。

这个方式一般常用频繁操作数据库,因为频繁的打开关闭数据库会影响程序的性能。

我们这博客主要是介绍第二种设计思路,即Sqlite数据库连接池的设计方案。

1》继承SQLiteOpenHelper类,实现它的相关重写方法,伪代码如下

public class XxxHelper extends SQLiteOpenHelper{

public 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(
c45b
) 返回的为 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,这样的目的很明显,就是当一条线程在写数据库时,其他线程只能等待,当写完释放连接后,才能继续访问数据库
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息