10

コンテンツプロバイダーからデータをプルする複数のアクティビティを持つアプリ(Android 2.2 Google APIレベル8)があります(データベースアクセスのみを選択してください)。また、データベース書き込みタスクを受け入れる中央ブロッキングタスクキューを備えたサービスもあります。アクティビティは、サービスリクエストを(インテントとして)起動できます。これにより、タスクがブロッキングキューに配置され、単一のスレッドによる順次取得と実行が可能になります。データベースは約4MBです。

サービスがデータベースへの書き込みを含むデータベースと対話するためのメソッドを呼び出すために使用する単一のデータベースヘルパーがあります。すべてのSQL書き込みは、データベースヘルパー内で実行されます。

  • すべてのデータベース書き込みはトランザクションに囲まれています。
  • すべてのデータベース読み取りでは、メソッドの最後でカーソルが閉じられます。
  • どのアクティビティにもデータベースオブジェクトへのハンドルはなく、コンテンツプロバイダーまたはサービスを介してのみ通信できます。
  • アクティビティなどのAlarmManagerが起動したタスクは、サービスを使用して適切なタスクをキューにポップするだけです。
  • このサービスは、データベースヘルパーへのハンドルを持つ唯一のクラスです。
  • すべてのデータベース書き込みは、キューに配置されたタスクを介してのみ実行されます。SQLiteデータベースへの同時書き込みを回避することが不可欠であることを十分に認識して、タスクの実行がシーケンシャルであることを徹底的に確認しました。

タスク実行の実行中に、「トランザクションの開始」のタスク実行によってトリガーされたデータベースへの書き込みを試行すると、一貫して1つまたは2つの「データベースがロックされています」エラーが発生します。

ロックの原因を突き止めようとしたところ、dbhelper.inTransaction()、dbhelper.isLockedByThisThread()、dbhelper.isLockedByOtherThread()を使用しても、予期しないデータベースロックが示されないため、役に立たなかったことがわかりました。

私が見つけたのは、ロックを早期に検出するのに役立ったのは、問題をログに記録するtry catchブロック内に、実際のSQL書き込みコードなしでbeginTransaction()とsetTransactionSuccessfulを使用してメソッドを作成することでした-常にbeginTransaction()によってトリガーされます。

このデータベースロックトラップを各ブロッキングキュータスクメソッドのいずれかの側に配置し、終了後にデータベースをロック状態のままにしていた単一の原因を見つけることを期待/期待しました。一貫した犯人を見つけることができませんでした。タスク呼び出しの開始からデータベース書き込みまでドリルダウンした後、以前に実行されたタスクによってロックされていなくても、データベースロックが一見突然発生する可能性があることがわかりました(これらのタスクはすべて、同じ単一のスレッドで順番に実行されます) )。

データベースロックの問題に関する他の多くの人々の経験を調べた後、すべてのタスクでトランザクションが完了した直後にデータベース接続を閉じようとしましたが、データベースロックがさらに発生するように思われる場合は、これは役に立ちませんでした。各タスクの実行の間にスリープを追加しようとしました。徹底的にテストされていませんが、一般的に3秒以上の遅延により、データベースロックの表示が停止するように見えることがわかりました。アラームマネージャーが起動したタスクを無効にしてみましたが、違いはありませんでした。

私が持っている印象は、アプリケーションの外部にある何らかの形式のメンテナンスタスクがデータベースに定期的にドロップインしてロックしていることです。おそらく、ログの書き込みが遅れています。明らかに、私はタスク処理の遅延を設定することに熱心ではないので、必要に応じてデータベースの書き込みを再試行するために、データベースロックの再試行タスクキューを用意することを検討しています。解決することを非常に好みますが、アイデアが不足しています。

誰かが私が逃したいくつかの原則や落とし穴を考えることができますか?

たまにデータベースロックが発生するのは、Androidや大規模なSQLiteデータベース内では実際には正常ですか?

ありがとう

4

3 に答える 3

7

SQLite は、単一のデータベース接続を使用している限り、複数のスレッドからのシーケンシャル アクセスを保証します。データベース接続をどこでどのように開いたり閉じたりしていますか?

通常、データベースは起動時に一度開き、決して閉じないことをお勧めします。SQLite のトランザクショナルな性質は、とにかく書き込みができるだけ早く永続ストレージにフラッシュされることを意味するため、クローズするメリットはありません。

于 2012-01-14T23:18:37.670 に答える
0

に関して

Android や大規模な SQLite データベース内でデータベース ロックが時々発生するのは、実際には正常なことですか?

いいえ、時折データベースがロックされるのは正常ではありません。あなたの話を読んで、サービスとコンテンツ プロバイダーの両方がデータベースからプルしていると言うので、2 つのアクセスの間でデータベースをロックしている可能性があります。

私が一般的に行っていることは、コンテンツ プロバイダーを介してすべてのデータベース アクセスを確実に処理することです。データベースへの単一のエントリ ポイントを持つことで、すべてのソフトウェア コンポーネントが同じロジックを使用して DB にアクセスできるようになります。コンテンツ プロバイダーを介してサービスが DB にアクセスすることは可能でしょうか?

また、DB をコンテンツ プロバイダーの背後に配置することで、複数のスレッドから一度にアクセスできることも覚えておくとよいでしょう。一度に 1 つのスレッドだけが DB にアクセスするようにするには、コンテンツ プロバイダー内の DB に同期構成を配置します。明らかに、DB に対して多くの長い書き込み/読み取りを行っている場合、この方法でロックするとアプリが完全に破壊されます。すべての DB コードをコンテンツ プロバイダー内に配置すると、単一のデバッグ ポイントが得られ、複数のスレッドが DB にアクセスしているかどうかを判断するのに役立ちます。

于 2012-01-19T19:59:11.433 に答える
0

DB を呼び出すアクティビティ、または DB を呼び出す他のアクティビティを呼び出すアクティビティにインスタンスが 1 つしかないかどうかを確認します。それ以外の場合は、ある意味でそれ自体をロックできます。

于 2012-01-14T22:52:08.740 に答える