2012-05-29 1 views
3

Я прочитал много тем в этом подзаголовке, но никто не мог ответить на мой вопрос.Параллельный доступ к базе данных SQLite в Android-db уже закрыт

Я обращаюсь к своей базе данных из параллельных потоков, мой SQLiteOpenHelper реализует шаблон дизайна синглтона, поэтому у меня есть только один экземпляр для моего приложения.

доступ к моей базе данных с кодом, как это:

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

Я не понимаю, почему я все еще получает «DB уже закрыт» ошибка, не метод getWritableDatabase() заблокировать базу данных до закрытия () называется ? Другие вызовы getWritableDatabase() из других потоков должны ждать, пока db не будет закрыт? Это правильно или я что-то пропустил?

ответ

7

Расширение ответа elhadi Я столкнулся с подобными проблемами при открытии и закрытии соединений с базой данных по нескольким асинхронным задачам. Из моего исследования в то время стало ясно, что нет необходимости постоянно открывать и закрывать соединения db. Подход, который я завершил, заключался в подклассификации Application и выполнении одиночного db, открытого во время onCreate, и одного db, закрывающего onTerminate. Затем я установил статический getter для извлечения уже открытого объекта SQLiteDatabase. Not DI (Dependency Injection) дружелюбный, но 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 это где-то, но это статический одиночный дб открыть/закрыть решить этот вопрос вы имеете. На SO есть еще один ответ, где описывается это решение.

Подробнее:

В ответ на комментарий Fr4nz в о с ниже NPE, я предоставил более подробную информацию о нашей конкретной реализации.

Короткая версия

Ниже «полная картина» трудно понять без хорошего понимания BroadcastReceivers. В вашем случае (и в качестве первого) добавьте в код создания базы данных и введите intialise и откройте базу данных после создания базы данных. Так пишите;

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

Длинная версия

Да, есть немного больше, чем просто приведенный выше код. Обратите внимание на мой комментарий об исключении catch в первом экземпляре (т. Е. При первом запуске приложения). Здесь говорится: «Наше приложение запускает событие, создающее задачу создания db». Что на самом деле происходит в нашем приложении, так это то, что прослушиватель (платформа Android BroadcastReceiver) зарегистрирован и одной из первых вещей, которые делает основной вид активности приложения, является проверка того, что статическая переменная в MainApplication не равна нулю. Если он равен нулю, тогда возникает асинхронная задача, которая создает db, который, когда он заканчивается (т.запускает метод 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, role-check есть база данных?
    • Да? переменная базы данных инициализируется
    • Нет? Получатель зарегистрирован (OpenDatabaseReceiver)
  2. Класс: MainActivity: Роль-приземление для приложения и изначально проверяет, что переменная базы данных не равна нулю.
    • database есть null? Не добавляет в фрагменты, которые выполняют ввод-вывод, и добавляет в диалоговом окне «создание базы данных приложений» или подобное.
    • database не имеет значения? Выполняется с основным потоком выполнения приложения, добавляет в списки, поддерживаемые db и т. Д.
  3. Класс: DatabaseCreationDialogFragment: role-spawns async task для создания базы данных.
    • Регистрирует новый приемник, прослушивающий, когда база данных была создана.
    • При сборе сообщения «Я создал базу данных» запускается другое событие (из получателя), сообщающее приложению открыть базу данных.
  4. Класс: MainApplication: role 2- прослушать сообщение, созданное «базами данных».
    • Получатель, описанный выше (OpenDatabaseReceiver), открывает базу данных и передает (другим событием!), Что база данных готова к использованию.
  5. Класс: MainActivity: role 2- Подбирает «базу данных готов», устраняет диалоги «мы создаем базу данных» и включаем отображение данных/функций в приложении.

Мир восстановлен.

+0

Не забудьте обновить свой '' '' Manifest.xml'''' путь к новому имени приложения, если вы попытаетесь это сделать. – OceanLife

+0

Спасибо за ответ – Fr4nz

+0

Решила ли ваша проблема Fr4nz? – OceanLife

1

Если вы вызываете DatabaseHelper.getInstance(). GetWritableDatabase() в своем потоке, я советую вам управлять им перед началом ваших потоков. вы открываете свой db в основной программе, вы вызываете свои потоки. после прекращения потоков вы закрываете свой db в основной программе.

+0

Да Я вызываю DatabaseHelper.getInstance(). GetWritableDatabase() в каждом из моего потока. Спасибо за ваш ответ. Я попробую это. Но теоретически было мое отражение? – Fr4nz

+0

В вашем решении я должен был вызвать getWritableDatabase(), прежде чем запускать потоки, чтобы инициировать базу данных? Я действительно не вижу, как это должно помочь, потому что мне все равно нужно вызвать getWritableDatabase() в моем потоке, не так ли? – Fr4nz

 Смежные вопросы

  • Нет связанных вопросов^_^