2011-04-07 3 views
11

私のアプリでは、UIと同期サービスを実装する必要があります。 これはバックグラウンドで実行され、データを更新します。同期サービスはあまり簡単ではなく、マルチスレッドを使用します。AndroidのSQLiteでマルチスレッドはどうですか?

これは私の話です。 このアプリケーションの開発を始めたとき、私はsqliteについて何も知らなかったので、Javaではスレッド同期を使用していません。結果: "SQLiteException:データベースがロックされている:BEGIN EXCLUSIVE;"のような多くの例外が発生しました。

次に、すべてのトランザクションを通常のJava synchronized(){}ブロックと同期させました。すべてがはるかに良くなった。しかし、CursorAdapterを実装するためにCursorを使用しました。だから、時々私は同じ "SQLiteException:データベースがロックされて:BEGIN排​​他的な取得していた"

私は、すべてのスレッドセーフなものを処理する小さなスレッドセーフなsqliteユーティリティを作成し終えました。また、私はArrayAdapterのようなものを使用しなければなりません(Cursorからすべてのデータを読み込み、読み込んだ後に閉じ、このブロックも同期させてください)。それで、うまくいきました。

しかし、私はUIを扱うのが好きではありません。このソリューションでは、UIが実際には遅くなっています。カーソルからいくらかのデータ量を読み込むのが速いですが、CursorAdapterを使うよりも遅いです。

だから、誰がこの質問の解決策を得ましたか? ありがとうございます

+0

をあなたはUIが遅くなった意味はどうすればよいですか?接触などに応答するのに時間がかかりますか、データベースデータを表示するのに時間がかかりますか?データベースを読んでいるだけの場合は、トランザクションを使用しないでください。これにより、DBから複数の読み込みを一度に実行できます。 –

+0

@Joseph Earl私は「データを表示するのに長い」という意味でした。はい、私はデータを読むときにトランザクションを使用しません。私はいくつかのデータを書くときにのみ使用します –

答えて

16

最後に、解決策が出ました。ここにあります。

私はいくつかのフォーラム、グーグルグループを読んで、sqliteデータベースは一度だけ開くべきであることを知りました。だから、私はこれをシングルトンで実装しました。

また、すべての書き込み操作を同期させるためのDBコードを実装しました(多くのスレッドが一度に書き込み操作を実行するのを防ぐため)。 そして、私はカーソルを開いてそれらから読むことに気をつけません。

何日かのテストの後、私は私のユーザーからのエラー報告を得なかっきたので、私はこれが問題だった私は、アプリケーション全体のSQLiteデータベースを何度も開いた私の前の仕事では、

をうまくいくと思います。

+0

だから...あなたは一度だけデータベースを開くと...'close()はデータベース '/data/data/com.package/databases/name.db'に対して明示的に呼び出されていませんでした android.database.sqlite.DatabaseObjectNotClosedException:アプリケーションがクローズしませんでした。ここで開かれたカーソルまたはデータベースオブジェクト ' – Cristian

+0

書き込み中は、@ tongenによる読み取りは許可されています。私は、私のアプリでシングルトンを使用してUIが非常に遅く応答している間にデータの読み取り中に書き込みが進行中です。 – Kishore

+0

@Kishoreあなたはいつでもデータを読むことができます。また...メインスレッドのデータを読み込まないでください。あなたはいつもバックグラウンドスレッドでデータを読むべきです –

5

SQLiteは排他的な書き込みロック、共有読み取りロックモデルを実装しています。これは、同時読者が同時にデータベース内でアクティブになることができることを意味します。あるいは、単一のライターであっても、両方を持つことはできません。 WALロギング機能を使用すると、同時に1つのライターと複数のリーダーがデータベースでアクティブになりますが、複数のライターを持つことはできません。 SQLite並行性herehereに関する優れたドキュメントがあります。

バークレーDBを検討することをお勧めします。 Berkeley DBは、SQLiteと完全に互換性のあるSQL APIを提供しています。実際には、Berkeley DBストレージレイヤーの上にSQLiteパーサー、プランナー、およびエグゼキュータを追加することです。これがSQLiteアプリケーション開発者に提供するのは、拡張性、並行性、信頼性(HA)などの機能を備えたSQLite互換のライブラリです。 Berkeley DBは複数のリーダーをサポートし、同時にデータベースにアクセスして書き込みを行います。バークレーDBとSQLite(Performance comparisonBehavioral Differences)を比較した「SQLiteへの完全なガイド」の著者Mike Owensが書いた2つの優れたホワイトペーパーがあります。

免責事項:私はBerkeley DBのプロダクトマネージャーの1つで、私は少し偏っています。しかし、あなたのような要求(より多くの並行性、スケーラビリティ、信頼性がSQLiteから必要とされている)は、まさに私たちがあなたに両方の世界のベストを提供する組み合わせライブラリを提供することに決めた理由です。

+1

ちょうど注記 - 完全なリンク(短縮されたものとは対照的に)が好ましいので、彼らがどこにつながるかを見ることができます。 Android実装へのリンクもありますか?乾杯! –

+0

答えをありがとう!しかし、私は私のソリューションがAndroidデバイスで動作する必要があります。私が理解しているように、あなたのソリューションはAndroid上で動作することはできません。 (と埋め込まれた特別なバージョンのsqlite) –

+0

@ジョセフ、提案に感謝します。私は短いリンクとフルリンクが使用されていることに気付きました。今日はAndroid用のBDBを構築し、SQLiteライブラリを置き換えるか、BDBをアプリケーションにリンクしてローカルに呼び出すことができます。これについての良いOTNの記事がここにあります:http://forums.oracle.com/forums/thread.jspa?messageID=9440046� – dsegleau

2

シングルトンヘルパークラスを使用してdbにアクセスする場合、自分自身を同期する必要はなく、ヘルパークラスは同期自体を管理するため、複数のリーダー/ライターからヘルパーを使用できます。

探しat this post MOR詳細な説明

+0

どのAndroidバージョンをお使いですか?以前のバージョンではこれに問題があったためです。 Googleがこれをいつ修正したかはわかりません。しかし、以前はあなた自身の同期を使用する必要があります。私はそれを使用していないときには失敗していたし、同期を追加した直後に修正された –

0

使用する接続を開くためのシングルトンのヘルパー。

1)読み取り可能な接続を必要なだけ開いて、終了したら閉じてください。

2)書き込み可能な接続の場合は、書き込み可能な接続を1つだけ開いてください。

書き込み可能な別の接続を開こうとすると、既に開いている接続が返されます。 書き込み可能な接続が存在しない場合は、新しい書き込み可能な接続を開きます。 すべてのスレッドが完了すると、writable_connectionカウンタを保持し、書き込み可能な接続を閉じます。

私はこれを実装しようとしており、完了した時点で正常に動作しているかどうかをすべて知っています。

0

さて、私の以前の答えは間違っていると思われるドキュメントを読んでください。 私は単一の(トン)SQLiteOpenHelperを持っているので、すべての接続が同じであるようです。 読み取り可能なものであっても、書き込み可能な接続と同じです。

私はgetReadableDatabaseの呼び出しをスキップし、getWritableDatabaseのみを使用します。 次に、データベースを1回だけ閉じるようにカウンタを保持します。

SQLiteOpenHelperは書き込みをシリアル化するので、このようにすべてうまくいくはずです。 私はちょうど私が最後に1つを開いたままにしていないことを確認するために、接続を追跡する必要があります。

だから私のDbHelperクラスで私が今持っている:

private int activeDatabaseCount = 0; 
public synchronized SQLiteDatabase openDatabase() { 
    SQLiteDatabase connection = getWritableDatabase(); // always returns the same connection instance 
    activeDatabaseCount++; 
    return connection; 
} 
public synchronized void closeDatabase(SQLiteDatabase connection) { 
    activeDatabaseCount--; 
    if (activeDatabaseCount == 0) { 
     if (connection != null) { 
      if (connection.isOpen()) { 
       connection.close(); 
      } 
     } 
    } 
} 
関連する問題