13

Android sqlite データベースと同時書き込みに重大な問題があります。より良い説明のために、実際の例を挙げます。

私はデータベースからアイテムのリストを表示しているデスクトップ ウィジェットを持っています (そして、バックグラウンドで、定期的にリモート サーバーから新しいデータを収集し、それらでデータベースを更新する DataService を持っています)。そのため、リスト内のアイテムをクリックすると、データベース内のクリックされたアイテムを更新する (= 書き込み操作を行う) 必要があります。しかし、DataService がデータベース内の新しいデータを更新しているときに、アイテムを正確にクリックすると、もちろん次のようなエラーがログに記録されます。

android.database.sqlite.SQLiteException: error code 5: database is locked

通常、シミュレートするのは困難ですが、たとえば 10 秒ごとに実行するように DataService をスケジュールすると (デモンストレーションのためだけに)、このエラーを非常に簡単にシミュレートできます。

私の質問は、これをどのように処理するかです。同時に2つの書き込みイベントがある場合、最初にのみ実行され、2番目はエラーとしてログに記録されることをドキュメントで読みました。奇妙に聞こえますが、別のオプションが必要です。たとえば、2 番目の書き込みは最初の書き込みが完了するまで待機します。それとも他の解決策ですか?ドキュメントを読み込もうとしていますが、この項目は Google ドキュメントでカバーされていないようです...私が持っているほとんどすべての情報は、公式ページ以外で見つけました。

PS: これは、私の DBHelper クラスの短縮版です。

public class DBHelper extends SQLiteOpenHelper {

    private static final String TABLE_NEWS = "News";    
    private static final String COL_ID = "id";
    private static final String COL_TITLE = "title";
    private static final String COL_ALERT = "alert";

    public DBHelper(Context context) {
        super(context, "MY_DB_NAME", null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE " + TABLE_NEWS + "(" + COL_ID + " TEXT PRIMARY KEY," + COL_TITLE + " TEXT," + COL_ALERT + " INTEGER" + ")");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NEWS);
        onCreate(db);
    }

    public void addRecords(ArrayList<NewsItem> items) {
        SQLiteDatabase db = this.getWritableDatabase();    
        for (int i = 0; i < items.size(); i++) {
            NewsItem item = items.get(i);    
            ContentValues values = new ContentValues();
            values.put(COL_ID, item.getId());
            values.put(COL_TITLE, item.getTitle());
            values.put(COL_ALERT, item.getAlertMe());    
            db.insert(TABLE_NEWS, null, values);
        }    
        db.close();
    }

    public int updateRecord(NewsItem item) {
        SQLiteDatabase db = this.getWritableDatabase();    
        ContentValues values = new ContentValues();
        values.put(COL_ALERT_ME, item.getAlertMe());
        int updated = db.update(TABLE_NEWS, values, COL_ID + " = ?", new String[] { item.getId() });
        db.close();    
        return updated;
    }
}
4

2 に答える 2

23

SQLiteDatabaseスレッド セーフを実現するには、すべてのスレッド (およびそれらのホスト コンポーネント) で単一のオブジェクトを使用する必要があります。この効果を得るには、シングルトンDBHelperにするか、を使用します。ContentProvider

于 2012-05-29T12:19:12.560 に答える
14

ContentProvider、このような理由で作成されました。挿入/更新/削除操作のために複数のスレッドから呼び出すことができます。

http://developer.android.com/reference/android/content/ContentProvider.html

ContentProvider多くの人は、データを共有したい場合にのみ を使用するべきだと感じています。これは の大きなメリットですが、メリットはContentProviderそれだけではありません。主な利点は、を使用する場合ContentProvider、Android がデータベース接続を管理することです。

これは、コンテンツ プロバイダーに関する優れたチュートリアルです。

http://www.vogella.com/articles/AndroidSQLite/article.html

注: ContentProvider の JavaDoc には、データを共有するために必要であると記載されていますが、データを共有するためだけに使用する必要があるという意味ではありません。Application Fundamentals のドキュメントでは、ContentProviders についてもこれを述べています。

コンテンツ プロバイダーは、アプリケーションにとってプライベートで共有されていないデータの読み取りと書き込みにも役立ちます。たとえば、Note Pad サンプル アプリケーションでは、コンテンツ プロバイダーを使用してメモを保存します。

http://developer.android.com/guide/topics/fundamentals.html#lcycles

于 2012-05-29T11:56:25.737 に答える