Android sqlite "база данных заблокирована", несмотря на использование поставщика контента и последовательного доступа к базе данных

У меня есть приложение (Android 2.2 Google API уровня 8), в котором есть несколько действий по извлечению данных из поставщика контента (доступ только к базе данных SELECT). Он также имеет службу с центральной блокировкой задачи, принимающей любые задачи записи базы данных; действия могут запускать запрос службы (как намерение), который ставит задачу в очередь блокировки для последовательного извлечения одним потоком и исполнением. База данных составляет около 4 Мб.

Существует один помощник базы данных, который служба использует для вызова методов для взаимодействия с базой данных, включая запись на нее; все записи SQL выполняются в хелпере базы данных.

  • Все записи базы данных окружены транзакцией.
  • Все чтения базы данных имеют курсор, закрытый в конце метода.
  • Ни одна из операций не имеет дескриптора объекта базы данных, они могут обмениваться данными только через поставщика контента или службы.
  • Любые запущенные задачи AlarmManager - например, "Действия" - используют эту службу только для вызова соответствующей задачи в очередь.
  • Служба - это единственный класс, который имеет дескриптор помощника базы данных.
  • Все записи базы данных выполняются только с помощью задачи, помещенной в очередь; Я исчерпывающе проверял, что выполнение задачи последовательное, хорошо известно, что это важно, чтобы избежать параллельной записи в базу данных SQLite.

Во время выполнения выполнения задач я последовательно получаю одну или две ошибки "базы данных заблокирован" при попытке записи в базу данных, вызванной выполнением задач "начать транзакцию".

При попытке отследить источник блокировки я обнаружил, что использование dbhelper.inTransaction(), dbhelper.isLockedByThisThread(), dbhelper.isLockedByOtherThread() не помогло, поскольку они не указывают на непредвиденную блокировку базы данных.

То, что я обнаружил, что раньше работал с обнаружением блокировки, заключалось в том, чтобы создать метод с beginTransaction() и setTransactionSuccessful без какого-либо реального кода записи SQL, в блоке catch try, который будет регистрировать проблему - всегда запускается beginTransaction().

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

Посмотрев на ряд других людей, сталкивающихся с проблемами блокировки базы данных, я попытался закрыть соединение с базой данных сразу после завершения транзакции по всем задачам, но это не помогло, если что-то, похоже, вызвало больше сбоев базы данных. Пробовал добавить спать между выполнением каждой задачи; не исчерпывающе проверены, но в целом обнаружили, что задержка в 3 секунды или выше, казалось, остановила появление блокировок базы данных. Пробовал отключить задачи диспетчера аварийных служб - не имеет никакого значения.

Впечатление, которое у меня есть, заключается в том, что некоторая форма задачи обслуживания, внешняя по отношению к моему приложению, периодически отбрасывает и блокирует базу данных - возможно, задерживает запись журналов. Очевидно, я меньше, чем увлекаюсь настройкой задержки обработки задачи, поэтому я рассматриваю возможность очереди задач повторной проверки базы данных для повторной загрузки базы данных при необходимости; многие предпочитают разрешать, но у меня заканчиваются идеи.

Может кто-нибудь подумать о каком-то принципе или о том, что я пропустил?

Является ли это на самом деле нормальным в Android и больших базах данных SQLite, что вы будете иногда блокировать базы данных?

Спасибо

Ответ 1

SQLite гарантирует последовательный доступ из нескольких потоков до тех пор, пока вы используете одно соединение с базой данных. Как и где вы открываете и закрываете соединение с базой данных?

Обычно я рекомендую открывать базу данных один раз при запуске и никогда не закрывать ее. Нет никакой выгоды для закрытия, так как транзакционный характер SQLite означает, что записи как можно скорее удаляются в постоянное хранилище.

Ответ 2

Я бы проверял, не работает ли какой-либо вид деятельности, вызывающий БД или вызывающий другую активность, вызывающую БД, только один экземпляр. В противном случае он может блокировать себя, в некотором смысле.

Ответ 3

Что касается

Является ли это на самом деле нормальным в Android и больших базах данных SQLite, что вы будете иногда блокировать базы данных?

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

Что я обычно делаю, так это то, что я обрабатываю весь доступ к базе данных через поставщика контента. Имея одну точку входа в базу данных, вы можете гарантировать, что каждый программный компонент использует одну и ту же логику для доступа к БД. Можно ли иметь доступ к службе через БД через поставщика контента?

Также хорошо помнить, что, поставив свою БД за контент-провайдером, ее все равно можно получить сразу несколькими потоками. Чтобы обеспечить доступ к БД только по одному потоку, вы можете разместить синхронизированные конструкции в БД внутри вашего поставщика контента. Очевидно, что если вы делаете много длинной записи/чтения в БД, блокировка таким образом полностью уничтожит ваше приложение. Ввод всего кода вашей базы данных внутри поставщика контента также даст вам одну точку отладки, которая поможет вам выяснить, обращаются ли к нему несколько потоков.