2012-05-29 52 views
3

我读了很多这个subjet的主题,但没有人能回答我的问题。并发访问Android中的SQLite数据库 - db已经关闭

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

访问我的数据库,这样的代码:

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

我不明白为什么我仍然得到“DB已经关闭”的错误,不应该getWritableDatabase()方法锁定数据库直到接近( ) 叫做 ?来自其他线程的其他getWritableDatabase()调用应该等到数据库关闭?这是对的还是我错过了什么?

回答

7

在elhadi的答案上展开我在跨多个异步任务打开和关闭数据库连接时遇到了类似的问题。从我当时的调查中可以清楚地看到,不必经常打开和关闭数据库连接。我最终采用的方法是对Application进行子分类,并在onCreate期间执行单个数据库打开和单个数据库关闭onTerminate。然后我设置一个静态获取器来检索已经打开的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回来我肯定有从什么地方plagerised这一点,但你有这个静态单分贝开/关解决了这个问题。在另一个解释这个解决方案的地方有另一个答案。

更多细节:

针对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框架)已注册,主要应用程序活动的第一件事情是检查MainApplication中的database静态变量是否为空。如果它是空的,那么会产生一个异步任务来创建数据库,当数据库完成时(即,运行onPostExecute()方法)最终触发我们知道将被我们在try-catch中注册的侦听器拾取的事件。接收者作为内部类生活在MainApplication类下,看起来像这样;

/** 
    * 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); 
     } 
     } 

因此,第一次安装的启动过程总结如此;

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

恢复和平。

+0

不要忘记更新你的'''''''Manifest.xml'''',如果你尝试这个,请使用新的应用程序“名称”的路径。 – OceanLife

+0

感谢您的回答 – Fr4nz

+0

它解决了您的问题Fr4nz? – OceanLife

1

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

+0

是的我在每个线程中调用DatabaseHelper.getInstance()。getWritableDatabase()。感谢您的回答,我会试试这个。但是理论上我的反思是对的吗? – Fr4nz

+0

在你的解决方案中,我应该在启动线程之前调用getWritableDatabase()以启动数据库?我真的不知道它应该如何帮助,因为我仍然需要在我的线程中调用getWritableDatabase(),对吧? – Fr4nz

相关问题