最近在项目中碰到一个新的模块,添加IM功能,在原有账号基层上添加一个IM账号,IM聊天的信息也需要保存在本地;
我们的做法是每个IM账号新建一个db文件,各自账号的消息存放在各自的数据库文件中,这样的好处是可以减小数据库文件的大小,查询数据的时候效率比较高。
不过这种做法的有一个需要注意的地方就是,
- 每次账号切换的时候需要重新加载数据库实例对象,不能把对象写死成 static class的类型,每次退出账号的时候要重置数据库实例对象,登录的时候新建。
- 若数据库是操作方法是以接口方法来做的话,在实例里面必须动态获取SQLiteOpenHelper对象,不要把它写成私有的成员变量。
具体实现步骤:
一、实现SQLiteOpenHelper类public class DBHelper extends SQLiteOpenHelper {
private static final int VERSION = 1;//数据库版本号
//用户IM历史记录表
private final String tUserImInfo = "create table if not exists useriminfo (" +
"id integer primary key autoincrement," +
"msgid text primary key not null," +
"caseid text")";
//用户IM历史记录视图
private final String vUserImInfo = "create view vuseriminfo as "
+ "select * from useriminfo order by caseid,timestamp desc,isim";
private static DBHelper helper;
private DBHelper(Context context) {
super(context, getDBName(), null, VERSION);
}
/**
* 数据库名字
*
* @return
*/
private static String getDBName() {
return "im_" + PreferenceUserBean.getUserId() + ".db";
}
/**
* 获取一个SQLiteOpenHelper的实例
*
* @return
*/
public static DBHelper getInstance() {
if (null == helper) {
synchronized (DBHelper.class) {
if (null == helper) {
helper = new DBHelper(IBingLiApplcation.getApplcationContext());
}
}
}
return helper;
}
/**
* 当用户退出、或被挤出APP,当换用户登录的时候就需要切换数据库,这时就需要重新建数据库
*
* @return
*/
public static void closeDBHelper() {
if (null != helper) {
helper.close();
}
helper = null;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(tUserImInfo);
db.execSQL(vUserImInfo);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//逐级迭代更新,防止跨版本更新的时候造成数据库不一致
switch (oldVersion) {
case 1:
version1To2(db);
case 2:
version2To3(db);
break;
}
}
}
采坑解析:
一开始的时候,没有注意到,我用的是一个静态内部类做的一个单实例,这样的话每次切换用户的时候,就不会在实例化一个对象,导致没有办法切换数据库。
错误代码:
public class DBHelper extends SQLiteOpenHelper {
private static class SingleHolder{
private static final DBHelper helper=new DBHelper();
}
private DBHelper getInstance(){
return SingleHolder.helper;
}
}
二、数据库接口实现
我采用接口的方法来统一处理增删改查的具体逻辑,这样方便扩展,也方便维护
public class IMInfoDaoImpl implements IMInfoDao {
private DBHelper getHelper() {
return DBHelper.getInstance();
}
@Override
public int queryUnReadCountByCaseID(String id) {
int total = 0;
String sql = "select count(*) total from tableName where status=0 and id=?";
String[] args = new String[]{id};
SQLiteDatabase db = getHelper().getReadableDatabase();
Cursor cursor = db.rawQuery(sql, args);
while (cursor.moveToNext()) {
total = cursor.getInt(cursor.getColumnIndex("total"));
}
cursor.close();
db.close();
return total;
}
@Override
public int queryUnReadCountByCaseType(int type) {
int total = 0;
String sql = "select count(*) total from tableName where status=0 and type=?";
String[] args = new String[]{String.valueOf(type)};
SQLiteDatabase db = getHelper().getReadableDatabase();
Cursor cursor = db.rawQuery(sql, args);
if (cursor.moveToNext()) {
total = cursor.getInt(cursor.getColumnIndex("total"));
}
cursor.close();
db.close();
return total;
}
…………………………………………………………
}
采坑解析:
刚开始,我获取DBHelper对象实例的时候是在IMInfoDaoImpl中写了是有成员变量,在new IMInfoDaoImpl()方法的时候就初始化了,这样做的后果就是,每当我退出账号A,不杀死APP的情况下,登录账号B,发现此时此刻明明已经加载了B的数据库,但是账号B的相关聊天数据,就是写到了A的库里面去了,这个问题纠结了我好几天,最后找到了这里DBHelper的对象必须动态获取,不能写成私有成员,每次都通过DBHelper.getInstance()去获取最新的对象。
错误代码:
public class IMInfoDaoImpl implements IMInfoDao {
private DBHelper helpser;
public IMInfoDaoImpl(){
helpser=DBHelper.getInstance();
}
@Override
public int queryUnReadCountByCaseType(int type) {
int total = 0;
String sql = "select count(*) total from tableName where status=0 and type=?";
String[] args = new String[]{String.valueOf(type)};
SQLiteDatabase db = helpser.getReadableDatabase();
Cursor cursor = db.rawQuery(sql, args);
if (cursor.moveToNext()) {
total = cursor.getInt(cursor.getColumnIndex("total"));
}
cursor.close();
db.close();
return total;
}
…………………………………………………………
}
三、账号登出时置空DBHelper对象,释放相关资源
DBHelper.closeDBHelper()
在此过程中,让我纠结郁闷很久的地方就是IMInfoDaoImpl里面DBHelper对象的问题,一开始写代码的时候没有意思到账号切换的问题,导致后来一系列的问题,用户收到了消息,再次登录历史记录又没有显示出来等问题,这里主要是动态获取DBHelper的对象就好了。