読者です 読者をやめる 読者になる 読者になる

ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

Android アプリ開発における SQLite のロックとマルチスレッドの話

Android アプリ SQLite プラクティス

Android アプリ開発で SQLite を使っていると、しばしば次のような例外が投げられることがあります。*1

android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)

軽く調べてみたところ

このエラーをぐぐってみると、「複数スレッドから SQLite を使う場合に、それぞれのスレッドで異なる SQLiteDatabase オブジェクトを使っているとこのエラーが出ることがある」 とか書かれていて、回避策として 「複数スレッドで SQLite を使う場合は 1 つの SQLiteDatabase オブジェクトを複数スレッドで使いまわすこと」 というような情報が得られました。 あとは当たり前ですが 「トランザクションがちゃんと閉じられているか確認すること」 とか。

いくつかの質問やブログ記事を見てみましたが、いずこも上のようなことが書かれていて、具体的にこの例外が発生する条件などがよくわかりませんでした。

あるスレッドでトランザクションを張っているときに別のスレッドで挿入などを行おうとすると 『SQLiteDatabaseLockedException が投げられます』、ということが書かれていたブログ記事も見つけたのですが、手元で確認したところ、トランザクションを張ってる時に別スレッドで insertOrThrow メソッドを呼び出しても必ずしもエラーになるわけじゃなくて、やっぱりよくわかんないなぁという感じでした。

そんなわけなのでちょっと詳細を調べてみることにしました。

調査

下記のようなテストコードを書いて動作を調べました。

次のような環境で実行しました。

API level によって待てる時間が違ってたりするっぽい (ちゃんと調べてない) ので、環境によっては一部のテストに失敗するかもしれません。

結果として

結論としては、次のような知見が得られました。

ベストプラクティス?

ベストプラクティス的なものはまだはっきりとは見えてないのですが、ぐぐって出てきた情報や今回調べたことなどを元に今のところわかっていることを書いておくと次のようになります。

詳細

*1:この例は API level 18 のもの。 API level 7 だと “android.database.sqlite.SQLiteException: error code 5: database is locked” というような例外

*2:API level 11 より前だと SQLiteException

*3:永遠に待ち続けるのかどうかは調べていない。

*4:API level 11 より前だと SQLiteException

*5:API level 11 より前だと SQLiteException