2012年2月3日金曜日

android における sqlite3 のスレッドによるアクセス

SQLiteOpenHelper を利用していて、IlligalStateException みたいなものに出くわしたので、スレッドによるアクセスは?データベースのインスタンスは?と、気になり、結局ソースコードを追った。ぐぐって見つけたのは、「SQLiteを使う場合の注意点」で、ここに書いてある事は概ね正しいです。  SQLiteOpenHelper.java にて SQLiteDatabase は、mDatabase というメンバ変数に保持しているので、1クラスにつき、1インスタンスしか扱えません。
データベースに対しては、読み書きを行うでしょうから、getWritableDatabase 関数をコールして、内部で保持しているデータベースのインスタンスを取得する事になります。スレッドで読取り専用であっても、データベースは、DBOpenHelperに対して、ひとつのインスタンスなのでしょうがないです。内部のコードを追っていると、DBOpenHelper::getReadableDatabase 関数の中から DBOpenHelper::getWritableDatabase をコールしている場所があるので、現時点では、あまり違いは無いかもしれません。ただし、現在どっちのモード(読取り専用かどうか)でデータベースを開いているかは、内部で管理しているみたいで、整合性が合わないと、IlligalStateException がスローされます。
カーソルを作成すると、データベースの読取領域がメモリ上に読み込まれる感じになるので、この同じデータベース・インスタンスに対して、別スレッドから同時アクセスしても、そんなに大きな問題にはならないでしょう。ただし、これが可能なのは、1つの書き手と残りは複数の読み手に限られます。書き込みトランザクション中に別スレッドから読取操作が行われながらトランザクションが完了するので、非常に気持ちの悪い動作になります。よって、常識的にコードを書くならば、スレッド1個に対してDBOpenHelperのインスタンスを1つ用意すべきでしょう。そうすれば、読取り専用と書き込みの区別もできます。
 あと、この辺の情報を検索していて、データベースを開いた状態でメンバ変数に保持する事や、カーソルを開いたままメンバ変数に保持するのは、ご法度だみたいな事を書いている人もいるみたいですが、これは間違いです。Android では、デバイスから得られる情報を取得するためのイベント・リスナ中の関数は、別スレッドになるので、ここら辺を意識できないでコードを組む人には、そのような説明をした方が都合が良いからだと思われます。

0 件のコメント: