前言

相信大家在开发过程中,也遇到过下面的这种异常:

java.lang.IllegalStateException:
attempt to re-open an already-closed object: SQLiteDatabase:

异常的解释:就是当你尝试打开一个可读可写的数据库时,该数据库已经被关闭,打开失败就会抛出该异常~

异常的原因:在我们开发过程中,会有很多数据需要在本地存储(像我们公司做的是教育软件,用户会产生大量的做题数据!)。如果需要操作大量的数据,SQLite肯定是首选,而且数据库的读写操作并不一定是按顺序去执行的。肯定会存在读和写并存的情况:有的线程在操作写入,有的线程在操作读取;假如有一条线程执行完后关闭了数据库,那么另一条线程就会抛出异常,因为数据库被关闭了。


在开发中,这种异步操作数据库的情况非常多,所以如果没有一个好的解决方法,项目的崩溃或异常数据会非常多。可能有些小伙伴会使用"try...catch..."处理,把异常都捕获,尽量避免了崩溃,但是这种情况会造成数据丢失(数据写入失败或数据读取失败)。那么,我们该如何解决这种问题呢?

下面就讲述下我要和大家说的这种解决方案:使用"AtomicInteger"控制数据库(SQLiteDatabase)的开和关~

先上代码(后面再详解)

首先:创建一个数据库操作对象

//创建一个本地数据库操作对象(SQLiteOpenHelper是android提供的一个帮助类,便于操作数据库)
public class SQLiteDBHelper extends SQLiteOpenHelper { public SQLiteDBHelper(Context context) {
//@ 参数2 name 数据库的文件名称
//@ 参数3 factory to use for creating cursor objects, or null for the default
//@ 参数4 version 数据库版本的控制 number of the database (starting at 1) Android4.0版本之后只能升不能降
super(context, "user.db", null, 1);
} //数据库第一次创建的时候,会执行 onCreate 方法(本地数据库删除后再创建也属于新建)
@Override
public void onCreate(SQLiteDatabase db) {
createTables(db);
} //数据库版本发生改变的时候,会执行 onUpgrade 方法
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
createTables(db);
} //创建表
private void createTables(SQLiteDatabase database) {
database.execSQL("CREATE TABLE IF NOT EXISTS user(id INTEGER PRIMARY KEY AUTOINCREMENT," +
"name TEXT,age INTEGER,sex INTEGER,number INTEGER,address TEXT);");
}
}

其次:创建一个数据开&关的帮助类

/**
* 数据库打开关闭帮助类
* 针对本地自建的数据库
*/
public class DBOpenManager {
private static DBOpenManager dbOpenManager;
private SQLiteDatabase database;
private AtomicInteger atomicInteger;
//用AtomicInteger来解决数据表异步操作的问题
private SQLiteDBHelper dbHelper; //私有化构造器
private DBOpenManager() {
initData();
} //初始化基本数据
private void initData() {
if (dbHelper == null) {
dbHelper = new SQLiteDBHelper(MyApplication.getAppContext());
}
if (atomicInteger == null) {
atomicInteger = new AtomicInteger();
}
} //单例模式获取操作类对象(懒汉式)
public static DBOpenManager getInstance() {
if (dbOpenManager == null) {
synchronized (DBOpenManager.class) {
if (dbOpenManager == null) {
dbOpenManager = new DBOpenManager();
}
}
}
return dbOpenManager;
} //打开数据库. 返回数据库操作对象
public synchronized SQLiteDatabase openDatabase() {
initData();
//查看当前 AtomicInteger 中的 value 值
Log.e("AtomicInteger", "开前:" + atomicInteger.get());
if (atomicInteger.incrementAndGet() == 1) {
try { //获取一个可读可写的数据库操作对象
database = dbHelper.getWritableDatabase();
dbHelper.getReadableDatabase();
} catch (Exception e) {
atomicInteger.set(0);
e.printStackTrace();
}
}
Log.e("AtomicInteger", "开后:" + atomicInteger.get());
return database;
} //关闭数据库
public synchronized void closeDatabase() {
//查看当前 AtomicInteger 中的 value 值
Log.e("AtomicInteger", "关前:" + atomicInteger.get());
if (atomicInteger.decrementAndGet() <= 0) {//避免关闭多次后数据库产生异常
atomicInteger.set(0);
Utils.closeCloseable(database);
database = null;
}
Log.e("AtomicInteger", "关后:" + atomicInteger.get());
}
}

Utils 里面的方法 closeCloseable 如下:

    public static void closeCloseable(Closeable closeable) {
try {
if (closeable != null) {
closeable.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}

测试

//new 一个线程延迟1, 打开数据库
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
SQLiteDatabase openDatabase = DBOpenManager.getInstance().openDatabase();
boolean isOpen = openDatabase.isOpen();
System.out.println("AtomicInteger +++++++++++++++++++++ " + isOpen);
}
}).start(); //Handler 延迟2秒,关闭数据库
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
DBOpenManager.getInstance().closeDatabase();
}
}, 2000); //new 一个线程也延迟2秒,再次打开数据库
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
SQLiteDatabase openDatabase = DBOpenManager.getInstance().openDatabase();
boolean isOpen = openDatabase.isOpen();
System.out.println("AtomicInteger --------------------- " + isOpen);
}
}).start();

ok

模板代码贴完了,下面具体说明下这种方案:

  1. 创建一个数据库操作对象。这一步大家都懂(我就不班门弄斧了

    最新文章

    1. ubuntu12.04安装搜狗输入法和配置
    2. php实现等比例不失真缩放上传图片
    3. 天使之城(codevs 2821)
    4. 电量显示Binding Converter MVVM
    5. [Express] Level 5: Route file
    6. [BZOJ 3282] Tree 【LCT】
    7. Android4.0的Alertdialog对话框,设置点击其他位置不消失
    8. 一个简单的多线程Python爬虫(一)
    9. windowns10安装httpd
    10. java @Override 报错解决
    11. windows使用.NET CORE下创建MVC,发布到linux运行
    12. C# Winform ListView控件
    13. org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].Standard
    14. 动态规划入门——数字三角形(Java)
    15. FireDac 组件说明一
    16. Python+selenium+webdriver 安装与环境配置
    17. [ios]NSThread传值 NSValue传值
    18. Numpy 创建数组2
    19. spring boot实战读书笔记1
    20. VC++ 6.0 C8051F340 MFC programming note

    热门文章

    1. mysql数据库磁盘空间被撑爆,创建定时任务定期释放资源
    2. Zabbix-Web监控介绍篇
    3. 纯数据结构Java实现(5/11)(Set&amp;Map)
    4. 一个接口多个实现类的Spring注入方式
    5. [WPF自定义控件库] 关于ScrollViewr和滚动轮劫持(scroll-wheel-hijack)
    6. 智能家居系列之——WIFI小车
    7. Codeforces 940D
    8. Leetcode之深度优先搜索(DFS)专题-515. 在每个树行中找最大值(Find Largest Value in Each Tree Row)
    9. 学习HTML之后的感受
    10. java设计模式5.组合模式、门面模式、享元模式、桥接模式