并发访问在Android的SQLite数据库 - 数据库已经关闭 [英] Concurrent access to a SQLite database in Android - db already closed

查看:203
本文介绍了并发访问在Android的SQLite数据库 - 数据库已经关闭的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我读了很多话题在这个subjet,但没有人能回答我的问题。

I read a lot of topics on this subjet but no one could answer my question.

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

I access my database from concurrent threads, my SQLiteOpenHelper implements the design pattern of the singleton so I have only one instance for my app.

访问我的数据库,code是这样的:

I access my database with code like this :

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

我不明白为什么我仍然得到DB已关闭的错误,不应该getWritableDatabase()方法锁定数据库,直到接近()被调用?其他getWritableDatabase()从其它线程调用应该等到该数据库被关闭?那是正确的还是我错过了什么?

I don't understand why I still get "db already closed" error, shouldn't the getWritableDatabase() method lock the database until close() is called ? Other getWritableDatabase() calls from other threads should wait until the db is closed ? Is that right or did I miss something ?

推荐答案

扩展在elhadi的回答打开和关闭多个异步任务数据库连接时,我也碰到过类似的问题。从我调查的时候很明显,没有必要不断地打开和关闭数据库连接。这种方法我最终采用了子类化应用程序,并在的onCreate 执行单个数据库打开和单个数据库紧密 onTerminate 。我再设置一个静态getter方法​​检索已经打开 SQLiteDatabase 对象。不DI(依赖注入),友好的,但Android无法真正做到这一点,但。

Expanding on elhadi's answer I have come across similar issues when opening and closing database connections across multiple async tasks. From my investigation at the time it became clear that it was unnecessary to constantly open and close db connections. The approach I ended up adopting was sub-classing Application and performing a single db open during onCreate and a single db close onTerminate. I then set up a static getter for retrieving the already opened SQLiteDatabase object. Not DI (Dependency Injection) friendly but Android can't really do that, yet.

这样的事情;

    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该从什么地方,但这种静态单一的分贝开/关解决了这个问题,您有。有对SO地方描述这个解决方案的另一个答案。

Reading the JavaDoc back I have certainly plagerised this from somewhere but this static single db open/close resolved this issue you are having. There is another answer on SO somewhere describing this solution.

更多细节:

在回应Fr4nz的评论大约低于NPE,我已经提供了具体实施的更多细节。

In response to Fr4nz's comment about an NPE below, I have provided more details of our specific implementation.

短版

下面的'全貌'很难不BroadcastReceivers有很好的理解把握。在你的情况(并作为第一关)加入您的数据库创建code和intialise并且您已经创建了数据库后打开数据库。所以写;

The below 'full picture' is difficult to grasp without a good understanding of BroadcastReceivers. In your case (and as a first off) add in your DB creation code and intialise and open the database after you have created the database. So write;

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

龙版

对了,还有更给它不仅仅是上述code一点点。请注意,我在一审异常俘获(即第一次应用程序运行时,永远)的评论。这里是说,我们的应用程序触发一个事件产卵分贝创建任务。究竟发生在我们的应用程序是一个监听器(Android的的BroadcastReceiver 框架)进行注册和主要应用程序的活动做的第一件事就是检查 MainApplication 数据库静态变量>不为空。如果为空,则异步任务催生了创建该数据库,它完成时,它(即运行 onPostExecute()法),最终触发,我们知道将在活动拾起我们在try-catch注册的监听器。在MainApplication类下的接收生活作为一个内部类,看起来像这样;

Yes, there is a little bit more to it than just the above code. Notice my comment on the exception catch in the first instance (i.e. the first time your application is run, ever). Here is says, "Our app fires an event spawning the db creation task". What actually happens in our app is that a listener (Android's BroadcastReceiver framework) is registered and one of the first things the main application activity does is check that the database static variable in MainApplication is not null. If it is null, then an async task is spawned that creates the db, which when it finishes (i.e. runs the onPostExecute() method) ultimately fires the event which we know will be picked up by the listener we registered in the try-catch. The receiver lives under the MainApplication class as an inner-class and look like this;

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

因此​​,启动过程中的第一次安装的总结是像这样;

So the summary of the start-up process for the first install is like so;

  1. 类:MainApplication,角色的检查有一个数据库?
    • 是?数据库变量初始化
    • 没有?接收登记(即 OpenDatabaseReceiver
  1. Class: MainApplication, role- check there is a database?
    • Yes? database variable is initialised
    • No? Receiver registered (the OpenDatabaseReceiver)
  • 数据库为空?不在于执行I / O的片段添加,并增加了在对话框创建应用程序数据库或类似的。
  • 数据库不为空?进行与主应用程序的执行流程,增加的房源由数据库等支持
  • database is null? Does not add in the fragments that perform I/O and adds in dialog saying "creating the application database" or similar.
  • database is not null? Carries on with main application execution flow, adds in listings backed by db etc
  • 在注册新的接收器收听已创建数据库时进行。
  • 在收集我已经创建了数据库消息触发另一个事件(从接收器),告诉应用程序打开数据库。
  • 在接收上述( OpenDatabaseReceiver )(由另外一个事件!)打开数据库和广播该数据库就可以使用了。
  • Receiver described above (OpenDatabaseReceiver) opens the database and broadcast (by another event!) that the database is ready to be used.

恢复和平。

这篇关于并发访问在Android的SQLite数据库 - 数据库已经关闭的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆