在Android中同时访问SQLite数据库 - 数据库已经关闭

时间:2012-05-29 14:24:40

标签: android sqlite sqliteopenhelper getwritabledatabase

我在这个子喷气机上阅读了很多主题,但没有人能回答我的问题。

我从并发线程访问我的数据库,我的SQLiteOpenHelper实现了单例的设计模式,所以我的app只有一个实例。

我使用以下代码访问我的数据库:

 SQLiteDatabase db = DatabaseHelper.getInstance().getWritableDatabase();
 ...
 Do some update in the DB
 ...
 db.close();

我不明白为什么我仍然得到“db already closed”错误,getWritableDatabase()方法是否应该锁定数据库,直到调用close()为止?来自其他线程的其他getWritableDatabase()调用应该等到db关闭?这是对的还是我错过了什么?

2 个答案:

答案 0 :(得分:7)

扩展elhadi的答案我在跨多个异步任务打开和关闭数据库连接时遇到了类似的问题。从我当时的调查中可以看出,不必经常打开和关闭数据库连接。我最终采用的方法是对Application进行子类化,并在onCreate期间执行单个数据库打开,并在单个数据库关闭onTerminate。然后我设置了一个静态getter来检索已经打开的SQLiteDatabase对象。不是DI(依赖注入)友好,但Android还不能真正做到这一点。

像这样;

    public class MainApplication extends Application {
          private static SQLiteDatabase database;

          /**
           * Called when the application is starting, before any other 
           * application objects have been created. Implementations 
           * should be as quick as possible...
           */
          @Override
          public void onCreate() {
          super.onCreate();
          try {
           database = SQLiteDatabase.openDatabase("/data/data/<yourdbpath>", null, SQLiteDatabase.OPEN_READWRITE);
          } catch (SQLiteException e) {
            // Our app fires an event spawning the db creation task...
           }
         }


          /**
           * Called when the application is stopping. There are no more 
           * application objects running and the process will exit.
           * <p>
           * Note: never depend on this method being called; in many 
           * cases an unneeded application process will simply be killed 
           * by the kernel without executing any application code...
           * <p>
           */
          @Override
          public void onTerminate() {
            super.onTerminate();
            if (database != null && database.isOpen()) {
              database.close();
            }
          }


          /**
           * @return an open database.
           */
          public static SQLiteDatabase getOpenDatabase() {
            return database;
          }
    }

回读JavaDoc我肯定从某个地方取消了这个,但是这个静态单个数据库打开/关闭解决了你遇到的这个问题。在SO上有另一个答案描述了这个解决方案。

更详细信息:

为了回应Fr4nz关于NPE的评论,我提供了有关具体实施的更多细节。

简短版

如果不理解BroadcastReceivers,很难掌握以下“全貌”。在您的情况下(作为第一个关闭)添加数据库创建代码并在创建数据库后初始化并打开数据库。所以写;

      try {
       database = SQLiteDatabase.openDatabase("/data/data/<yourdbpath>", null, SQLiteDatabase.OPEN_READWRITE);
      } catch (SQLiteException e) {
        // Create your database here!
        database = SQLiteDatabase.openDatabase("/data/data/<your db path>", null, SQLiteDatabase.OPEN_READWRITE);
       }
     }

长版

是的,除了上面的代码之外还有一点点。请注意我对第一个实例中的异常捕获的注释(即,第一次运行应用程序)。这里说,“我们的应用程序会触发一个产生数据库创建任务的事件”。在我们的应用程序中实际发生的是注册了一个监听器(Android的BroadcastReceiver框架),主要应用程序活动的第一件事就是检查database中的MainApplication静态变量是否为onPostExecute()不是空的。如果它为null,则会生成一个创建db的异步任务,当它完成时(即运行 /** * Listener waiting for the application to finish * creating the database. * <p> * Once this has been completed the database is ready for I/O. * </p> * * @author David C Branton */ public class OpenDatabaseReceiver extends BroadcastReceiver { public static final String BROADCAST_DATABASE_READY = "oceanlife.core.MainApplication$OpenDatabaseReceiver.BROADCAST_DATABASE_READY"; /** * @see android.content.BroadcastReceiver#onReceive(android.content.Context, android.content.Intent) */ @Override public void onReceive(final Context context, final Intent intent) { Log.i(CreatedDatabaseReceiver.class.getSimpleName(), String.format("Received filter event, '%s'", intent.getAction())); database = SQLiteDatabase.openDatabase("/data/data/<your db path>", null, SQLiteDatabase.OPEN_READWRITE); unregisterReceiver(openDatabaseReceiver); // Broadcast event indicating that the creation process has completed. final Intent databaseReady = new Intent(); databaseReady.setAction(BROADCAST_DATABASE_READY); context.sendBroadcast(databaseReady); } } 方法)最终会触发我们知道将由我们在try中注册的侦听器拾取的事件-抓住。接收器作为内部类存在于MainApplication类下,看起来像这样;

OpenDatabaseReceiver

所以第一次安装的启动过程总结就是这样;

  1. 类:MainApplication,角色检查有数据库吗?
    • 是?数据库变量已初始化
    • 否?接收者已注册(database
  2. 类:MainActivity:应用程序的角色登陆活动,最初检查数据库变量是否为空。
    • database为空?不添加执行I / O的片段,并在“创建应用程序数据库”或类似内容的对话框中添加。
    • OpenDatabaseReceiver不是空的?继续执行主应用程序执行流程,添加db等支持的列表
  3. 类:DatabaseCreationDialogFragment:role-产生异步任务来创建数据库。
    • 注册新的接收方,监听数据库的创建时间。
    • 在收集“我已创建数据库”消息时,会触发另一个事件(来自接收方),告诉应用程序打开数据库。
  4. 类:MainApplication:role 2-侦听“数据库创建”消息。
    • 上述接收者({{1}})打开数据库并广播(通过另一个事件!)数据库已准备好使用。
  5. 类:MainActivity:角色2-挑选“数据库准备就绪”的用法,摆脱“我们正在创建数据库”对话框,继续在应用程序中显示数据/功能。
  6. 恢复和平。

答案 1 :(得分:1)

如果你打电话     DatabaseHelper.getInstance()。getWritableDatabase() 在你的线程中,我建议你在开始你的线程之前管理它。你在主程序中打开你的数据库,你打电话给你的线程。线程终止后,在主程序中关闭数据库。