前言

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

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

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

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


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

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

先上代码(后面再详解)

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

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

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

  1. /**
  2. * 数据库打开关闭帮助类
  3. * 针对本地自建的数据库
  4. */
  5. public class DBOpenManager {
  6. private static DBOpenManager dbOpenManager;
  7. private SQLiteDatabase database;
  8. private AtomicInteger atomicInteger;
  9. //用AtomicInteger来解决数据表异步操作的问题
  10. private SQLiteDBHelper dbHelper;
  11. //私有化构造器
  12. private DBOpenManager() {
  13. initData();
  14. }
  15. //初始化基本数据
  16. private void initData() {
  17. if (dbHelper == null) {
  18. dbHelper = new SQLiteDBHelper(MyApplication.getAppContext());
  19. }
  20. if (atomicInteger == null) {
  21. atomicInteger = new AtomicInteger();
  22. }
  23. }
  24. //单例模式获取操作类对象(懒汉式)
  25. public static DBOpenManager getInstance() {
  26. if (dbOpenManager == null) {
  27. synchronized (DBOpenManager.class) {
  28. if (dbOpenManager == null) {
  29. dbOpenManager = new DBOpenManager();
  30. }
  31. }
  32. }
  33. return dbOpenManager;
  34. }
  35. //打开数据库. 返回数据库操作对象
  36. public synchronized SQLiteDatabase openDatabase() {
  37. initData();
  38. //查看当前 AtomicInteger 中的 value 值
  39. Log.e("AtomicInteger", "开前:" + atomicInteger.get());
  40. if (atomicInteger.incrementAndGet() == 1) {
  41. try { //获取一个可读可写的数据库操作对象
  42. database = dbHelper.getWritableDatabase();
  43. dbHelper.getReadableDatabase();
  44. } catch (Exception e) {
  45. atomicInteger.set(0);
  46. e.printStackTrace();
  47. }
  48. }
  49. Log.e("AtomicInteger", "开后:" + atomicInteger.get());
  50. return database;
  51. }
  52. //关闭数据库
  53. public synchronized void closeDatabase() {
  54. //查看当前 AtomicInteger 中的 value 值
  55. Log.e("AtomicInteger", "关前:" + atomicInteger.get());
  56. if (atomicInteger.decrementAndGet() <= 0) {//避免关闭多次后数据库产生异常
  57. atomicInteger.set(0);
  58. Utils.closeCloseable(database);
  59. database = null;
  60. }
  61. Log.e("AtomicInteger", "关后:" + atomicInteger.get());
  62. }
  63. }

Utils 里面的方法 closeCloseable 如下:

  1. public static void closeCloseable(Closeable closeable) {
  2. try {
  3. if (closeable != null) {
  4. closeable.close();
  5. }
  6. } catch (IOException e) {
  7. e.printStackTrace();
  8. }
  9. }

测试

  1. //new 一个线程延迟1, 打开数据库
  2. new Thread(new Runnable() {
  3. @Override
  4. public void run() {
  5. try {
  6. Thread.sleep(1000);
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. SQLiteDatabase openDatabase = DBOpenManager.getInstance().openDatabase();
  11. boolean isOpen = openDatabase.isOpen();
  12. System.out.println("AtomicInteger +++++++++++++++++++++ " + isOpen);
  13. }
  14. }).start();
  15. //Handler 延迟2秒,关闭数据库
  16. new Handler().postDelayed(new Runnable() {
  17. @Override
  18. public void run() {
  19. DBOpenManager.getInstance().closeDatabase();
  20. }
  21. }, 2000);
  22. //new 一个线程也延迟2秒,再次打开数据库
  23. new Thread(new Runnable() {
  24. @Override
  25. public void run() {
  26. try {
  27. Thread.sleep(2000);
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. SQLiteDatabase openDatabase = DBOpenManager.getInstance().openDatabase();
  32. boolean isOpen = openDatabase.isOpen();
  33. System.out.println("AtomicInteger --------------------- " + isOpen);
  34. }
  35. }).start();

ok

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

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

    解决Android数据库异步操作的大问题的更多相关文章

    1. Android数据库专家秘籍(七)经验LitePal查询艺术

      转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/40153833 经过了多篇文章的学习,我们已经把LitePal中的绝大部分内容都掌握 ...

    2. Android数据库框架——ORMLite轻量级的对象关系映射(ORM)Java包

      Android数据库框架--ORMLite轻量级的对象关系映射(ORM)Java包 事实上,我想写数据库的念头已经很久了,在之前写了一个答题系统的小项目那只是初步的带了一下数据库,数据库是比较强大的, ...

    3. 自己动手写Android数据库框架

      前言 相信不少开发人员跟我一样,每次都非常烦恼自己写数据库,并且那些数据库语句也经常记不住.当然网上也有非常多非常好的数据库框架,你能够直接拿来用,可是 非常多时候我们的项目.特别是一个小型的Andr ...

    4. Android数据库高手秘籍(三)——使用LitePal升级表

      转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/39151617 在上一篇文章中,我们学习了LitePal的基本使用方法,体验了使用框 ...

    5. Android数据库高手秘籍(二):创建表和LitePal的基本用法

      原文:http://blog.jobbole.com/77157/ 上一篇文章中我们学习了一些Android数据库相关的基础知识,和几个颇为有用的SQLite命令,都是直接在命令行操作的.但是我们都知 ...

    6. Android权限管理之RxPermission解决Android 6.0 适配问题

      前言: 上篇重点学习了Android 6.0的运行时权限,今天还是围绕着Android 6.0权限适配来总结学习,这里主要介绍一下我们公司解决Android 6.0权限适配的方案:RxJava+RxP ...

    7. 深入解析Sqlite的完美替代者,android数据库新王者——Realm

      写在前面: 又到一年一度七夕虐狗节,看着大家忍受着各种朋友圈和QQ空间还有现实生活中的轮番轰炸,我实在不忍心再在这里给大家补刀,所以我觉得今天不虐狗,继续给大家分享有用的. 如果你比较关心androi ...

    8. [Android Pro] 完美Android Cursor使用例子(Android数据库操作)

      reference to : http://www.ablanxue.com/prone_10575_1.html 完美 Android Cursor使用例子(Android数据库操作),Androi ...

    9. 解决Android中No resource found that matches android:TextAppearance.Material.Widget.Button.Inverse问题

      解决Android中No resource found that matches android:TextAppearance.Material.Widget.Button.Inverse问题http ...

    随机推荐

    1. npm install 安装很慢

      npm install 安装很慢 设置国内镜像 npm config set registry https://registry.npm.taobao.org npm install

    2. 导入spark2.3.3源码至intellij idea

      检查环境配置 maven环境 2.检查scala插件 没有的话可以到https://plugins.jetbrains.com/plugin/1347-scala/versions 下载与idea对应 ...

    3. 狄利克雷卷积&莫比乌斯反演证明

      狄利克雷卷积简介 卷积这名字听起来挺学究的,今天学了之后发现其实挺朴实hhh. 卷积: "(n)"表示到n的一个范围. 设\(f,g\)是两个数论函数(也就是说,以自然数集为定义域 ...

    4. 【CSS】Houdini, CSS的成人礼

      前情提要 CSS:老板,你看ES9,ES10都出来了,您看我的事情什么时候... W3C: 这不是正在走着流程嘛!小C你不要心急! W3C:(语重心长)你看啊,我们先(1)提个开发提案章程, 然后再批 ...

    5. 一个最简单的通过自定义注解形式实现AOP的例子

      1.首先实现AOP实例的第一步即声明切面类,两种方式(1.基于注解形式@Aspect,2.基于xml配置,一般都通过注解来声明切面类) 2.切入点表达式大致也有两种,一种是直接根据方法的签名来匹配各种 ...

    6. spring中的事件 applicationevent 讲的确实不错(转)

      event,listener是observer模式一种体现,在spring 3.0.5中,已经可以使用annotation实现event和eventListner里. 我们以spring-webflo ...

    7. Java 并发编程(一):摩拳擦掌

      这篇文章的标题原本叫做——Java 并发编程(一):简介,作者名叫小二.但我在接到投稿时觉得这标题不够新颖,不够吸引读者的眼球,就在发文的时候强行修改了标题(也不咋滴). 小二是一名 Java 程序员 ...

    8. C/C++ 修改系统时间,导致sem_timedwait 一直阻塞的问题解决和分析

      修改系统时间,导致sem_timedwait 一直阻塞的问题解决和分析 介绍 最近修复项目问题时,发现当系统时间往前修改后,会导致sem_timedwait函数一直阻塞.通过搜索了发现int sem_ ...

    9. Leetcode之深度优先搜索(DFS)专题-301. 删除无效的括号(Remove Invalid Parentheses)

      Leetcode之深度优先搜索(DFS)专题-301. 删除无效的括号(Remove Invalid Parentheses) 删除最小数量的无效括号,使得输入的字符串有效,返回所有可能的结果. 说明 ...

    10. C# winform语音提示

      1.SpeechSynthesizer文字转音频 项目添加引用:System.Speech using(SpeechSynthesizer speech = new SpeechSynthesizer ...