2013/06/02

SQLiteDatabase.openDatabase() に OPEN_READONLY 属性を付けているのに "attempt to write a readonly database" が発生することがある

私が GooglePlay に出しているアプリ SQLiteViewer に以下のようなクラッシュレポートが届きました。

java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=0, result=-1, data=Intent { dat=xxxx }} to activity {com.kokufu.android.apps.sqliteviewer.free/com.kokufu.android.apps.sqliteviewer.base.MainActivity}: android.database.sqlite.SQLiteException: attempt to write a readonly database
at android.app.ActivityThread.deliverResults(ActivityThread.java:2532)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:2574)
at android.app.ActivityThread.access$2000(ActivityThread.java:117)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:961)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:130)
at android.app.ActivityThread.main(ActivityThread.java:3683)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:897)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:655)
at dalvik.system.NativeStart.main(Native Method)
Caused by: android.database.sqlite.SQLiteException: attempt to write a readonly database
at android.database.sqlite.SQLiteDatabase.dbopen(Native Method)
at android.database.sqlite.SQLiteDatabase.(SQLiteDatabase.java:1849)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:820)
at com.kokufu.android.apps.sqliteviewer.base.b.a(ProGuard:28)
at com.kokufu.android.apps.sqliteviewer.base.MainActivity.c(ProGuard:631)
at com.kokufu.android.apps.sqliteviewer.base.MainActivity.a(ProGuard:471)
at com.kokufu.android.apps.sqliteviewer.base.MainActivity.onActivityResult(ProGuard:213)
at android.app.Activity.dispatchActivityResult(Activity.java:3935)
at android.app.ActivityThread.deliverResults(ActivityThread.java:2528)
... 11 more

発生したのは SQLiteDatabase.openDatabase()。
この手のアプリは、開こうとしたファイルによってはエラーが出てしまうのは仕方の無いことですとは言え、クラッシュして良いわけではないので、最新版ではクラッシュしないように修正済です。
SQLiteException が RuntimeException なのは間違いだと思う。


ただ、よく見てみると、Caused by のところに、attempt to write a readonly database との文字が。
コードの該当箇所は以下で、OPEN_READONLY 属性がついているのにも関わらずです。

            db = SQLiteDatabase.openDatabase(
                    uri.getPath(),
                    null,
                    SQLiteDatabase.OPEN_READONLY | SQLiteDatabase.NO_LOCALIZED_COLLATORS);

「そんなわけないだろー」と思いながら検索してみると、以下のような情報が。

なるほど。LG-P500 (Android 2.3.3) にあるバグなのね。
で、以下のようにすれば回避可能と。

try {
    db = SQLiteDatabase.openDatabase(
            uri.getPath(),
            null,
            SQLiteDatabase.OPEN_READONLY | SQLiteDatabase.NO_LOCALIZED_COLLATORS);
} catch (SQLiteException e) {
    final String message = e.getMessage();
    if (message == null) {
       throw e;
     }
     if (!message.contains("attempt to write a readonly database")) {
       throw e;
     }
    // データベースを read-only モードで開こうとしたのにも関わらず、
    // 失敗することがあります。
    // これは、LG-P500 Release:2.3.3 Sdk:10. にあるバグのためです。
    // openDatabase メソッドは readonly で開いたデータベースに
    // 書き込みを行おうとします。
    // これは1度だけのようなので、1回 readwrite モードで開いた後、
    // 閉じて、さらにもう1度 readonly で開きます。
    db = SQLiteDatabase.openDatabase(
            uri.getPath(),
            null,
            SQLiteDatabase.OPEN_READWRITE);
    db.close();
    db = SQLiteDatabase.openDatabase(
            uri.getPath(),
            null,
            SQLiteDatabase.OPEN_READONLY | SQLiteDatabase.NO_LOCALIZED_COLLATORS);
}

何か、特定の機種のバグ回避のために、READWRITE でデータベースを開くのは気が進まないんですけど、仕方ありません。
まー、そもそも、クラッシュレポートに機種情報が載っていなかったので、LG-P500 の問題だったのかどうかもわからないのですが…

0 件のコメント: