DataStoreとは何か、データ保存の基本とSharedPreferencesの置き換え

著者: IT Sectr 公開日: 2026-03-12 読了時間: 9 分

DataStoreは、Androidアプリケーションで少量のデータを保存するために設計されたJetpackライブラリのコンポーネントです。SharedPreferencesとは異なり、非同期で動作し、同時アクセス時のデータの一貫性を保証します。Google、2024によると、DataStoreはKotlin CoroutinesとFlowを使用しており、メインスレッドに対して安全でリアクティブアーキテクチャに適しています。

重要なポイント

  • DataStore — 非同期APIとProtocol Buffersによるデータ型を持つSharedPreferencesの置き換え
  • Preferences DataStore — Flowとトランザクションによる読み取りを備えたシンプルなキー値ストア
  • Proto DataStore — 自動スキーママイグレーションを備えた型付きストレージ
  • SharedPreferences — 大量データでUIスレッドをブロックする同期API
  • マイグレーションはSharedPreferencesMigrationインターフェースを通じてデータ損失なく実行されます

DataStoreとは?

DataStoreは、Androidでのローカルデータ保存のためのGoogleのソリューションであり、2020年にSharedPreferencesの代替として発表されました。2つのモードをサポートしています:Preferences DataStore(単純なキーと値のペア)とProto DataStore(Protocol Buffersに基づく型付きスキーマ)です。

主な利点は完全な非同期性です:すべての読み取り操作はKotlin CoroutinesからFlowを返し、書き込みはコルーチンコンテキストで実行されます。これにより、大量のデータを扱う際にSharedPreferencesの典型的な問題であったメインスレッドのブロッキングが排除されます。

DataStoreは操作の原子性を保証します:トランザクションモデルにより、同時書き込みがデータ損失を引き起こすことはありません。2つのコンポーネントが同時に同じ値を変更した場合、DataStoreはcompare-and-swapメカニズムを通じて競合を適切に処理します。

Google I/O 2023によると、DataStoreは新しいAndroidプロジェクトの40%で使用されており、Googleは設定保存の安定性が必要なすべてのアプリケーションでSharedPreferencesからの移行を推奨しています。

DataStoreのアーキテクチャ

DataStoreの中核にはSingleProcessDataStoreがあります — 単一プロセス内で動作する実装です。ファイルレベルのロックを使用したファイルベースのストレージを採用しており、データ書き込み時にファイルがロックされ、同時アクセス時の破損を防ぎます。

DataStoreはデシリアライゼーションエラーを自動的に処理します:ファイルが破損している場合はデフォルト値を返し、ファイルを上書きします。この動作は、DataStore作成時に設定できるcorruptionHandlerを介して構成可能です。

DataStoreが解決するSharedPreferencesの問題

SharedPreferencesは3つの根本的な問題を抱えています:メインスレッドでの同期ディスク読み取り、同時書き込み時の原子性保証の欠如、変更をリアクティブに追跡できないこと。DataStoreは3つすべてを解決します:観測にはFlow、原子性にはファイルロック、スレッドセーフには非同期API。

AndroidでDataStoreはどのように動作するか?

DataStoreはデバイスの内部ストレージにあるファイルにデータを保存します。Preferences DataStoreはSharedPreferencesと同様のファイル形式を使用しますが、整合性チェックのための追加メタデータが付いています。Proto DataStoreはバイナリのProtocol Buffers形式を使用し、ファイルサイズを削減してシリアライズを高速化します。

データ読み取り時、DataStoreはファイル全体を一度メモリにロードし、その後サブスクライバーはFlowを介して現在の状態を受け取ります。変更はすべてのアクティブなサブスクライバーに自動的にブロードキャストされます — SharedPreferencesのように手動でリスナーを登録する必要はありません。

Preferences DataStoreの仕組み

Preferences DataStoreはMapベースの組み込みシリアライズメカニズムを使用します。各エントリーは文字列とプリミティブ型(Int、Boolean、Float、Long、String、Set)のペアです。データはSharedPreferencesと同様のXMLファイルに保存されますが、ファイルロックによるアトミックな書き込みが行われます。

Preferences DataStore作成の例:ContextのpreferencesDataStore拡張機能がファイル名でシングルトンを作成します。繰り返し呼び出すと、同じインスタンスが返されます — これによりファイルの重複と異なるストレージインスタンス間の混乱が排除されます。

Proto DataStoreの仕組み

Proto DataStoreは、.protoファイルを介したデータスキーマの定義と、protobufプラグインを使用したコンパイルが必要です。生成されたJavaクラスはすべてのフィールドの唯一のエントリポイントとして使用され、SharedPreferencesでよくあるキーのタイプミスを排除します。

Proto DataStoreのスキーマは一度定義され、古いデータを失うことなく新しいフィールドの追加をサポートします。アプリの新しいバージョンがデフォルト値を持つフィールドを追加した場合、古いファイルは正しくデシリアライズされます — 後方互換性はプロトコルに組み込まれています。

Preferences DataStoreとProto DataStoreの比較

Preferences DataStoreProto DataStoreの選択は、データの複雑さと型付け要件に依存します。どちらのオプションも非同期でトランザクショナルですが、型安全性とシリアライズパフォーマンスが異なります。

特性Preferences DataStoreProto DataStore
型付け弱い(キー値)強い(生成クラス)
シリアライズXML(組み込み)Protocol Buffers(protobuf)
ファイルサイズ大(読み取り可能なXML)小(バイナリ)
複雑さ低(.proto不要)中(.protoが必要)
スキーママイグレーションスキーマなし自動(proto)
互換性SharedPreferences(マイグレーション経由)Proto DataStoreのみ

Preferences DataStoreを選ぶべき時

Preferences DataStoreはシンプルな設定に適しています:機能フラグ、認証トークン文字列、アプリ起動回数。データが少なく(最大10〜15キー)、厳密なスキーマを必要としない場合、Preferences DataStoreはprotobufプラグインを接続せずに最小の導入障壁を提供します。

Proto DataStoreを選ぶべき時

Proto DataStoreは、データ構造が複雑な場合やアプリのバージョン間で変更される可能性がある場合に適しています。例えば、ユーザープロファイル設定や20以上のフィールドを持つA/Bテスト設定などです。Protobufは強力な型付けと自動マイグレーションを提供し、キーの不一致による実行時エラーを排除します。

SharedPreferencesからDataStoreへの移行方法

GoogleはSharedPreferencesMigrationクラスを通じて組み込みの移行メカニズムを提供しています。移行はアプリ更新後の初回起動時に1回実行されます:DataStoreはSharedPreferencesからデータを読み取り、自身の形式で書き込み、移行を完了としてマークします。

移行はカスタム変換をサポートしています:SharedPreferencesのキーが目的のDataStoreキーと一致しない場合、SharedPreferencesMigrationを介して変換関数を指定できます。これにより、移行中にキーの名前変更やデータ型の変更が可能です。

ステップバイステップの移行

最初に、build.gradleにDataStoreを追加し、移行付きのDataStoreインスタンスを作成します:SharedPreferencesMigrationはSharedPreferencesファイル名と移行するキーのセットを受け入れます。2番目に、SharedPreferencesを通じて動作するすべてのコードを削除し、DataStore呼び出しに置き換えます。3番目に、移行をテストします:初回起動時にDataStoreにデータが表示され、古いSharedPreferencesファイルは使用されなくなります。

kotlin
val Context.dataStore by preferencesDataStore(
    name = "settings",
    produceMigrations = { context ->
        listOf(
            SharedPreferencesMigration(context, "old_prefs")
        )
    }
)

コードでのDataStore使用例

DataStoreは既存のプロジェクトに簡単に統合できます。以下に、Preferences DataStoreとProto DataStoreの実用的な例を示します — どちらもデータの読み取り、書き込み、リアクティブな観測を実演しています。

Preferences DataStore:設定の読み取りと書き込み

この例では、Preferences DataStoreが3つの設定を保存します:ダークテーマ、ユーザー名、起動回数。読み取りはFlowを返す.data拡張機能を介して行われます。書き込みは変更の原子性を保証する.editサスペンド関数を介して行われます。

kotlin
val Context.settingsDataStore by preferencesDataStore(name = "settings")

val isDarkMode: Flow<Boolean> = settingsDataStore.data
    .map { preferences ->
        preferences[booleanPreferencesKey("dark_mode")] ?: false
    }

suspend fun toggleDarkMode() {
    settingsDataStore.edit { prefs ->
        val current = prefs[booleanPreferencesKey("dark_mode")] ?: false
        prefs[booleanPreferencesKey("dark_mode")] = !current
    }
}

Proto DataStore:スキーマと使用法

Proto DataStoreは.protoファイルの定義が必要です。コンパイル後、読み取りと書き込みに使用されるUserSettingsクラスが生成されます。スキーマのバージョンマイグレーションは同じ.protoファイルで記述され、自動的に適用されます。

kotlin
// user_preferences.proto
syntax = "proto3";

message UserPreferences {
    string display_name = 1;
    int32 notification_count = 2;
    bool notifications_enabled = 3;
}

// DataStoreからの読み取り
val userPreferencesFlow: Flow<UserPreferences> =
    protoDataStore.data

// 新しい値の書き込み
suspend fun updateDisplayName(name: String) {
    protoDataStore.updateData { prefs ->
        prefs.toBuilder()
            .setDisplayName(name)
            .build()
    }
}

変更のリアクティブな観測

DataStoreはViewModelを介してMVVMアーキテクチャと統合します。DataStoreからのFlowは.stateInを介して収集され、UIで使用されます。データが変更されるたびに、UIは自動的に更新されます — 手動更新やLiveDataは必要ありません。

kotlin
class SettingsViewModel(
    private val dataStore: DataStore<Preferences>
) : ViewModel() {

    val uiState: StateFlow<SettingsUiState> =
        dataStore.data
            .map { prefs ->
                SettingsUiState(
                    isDarkMode = prefs[booleanPreferencesKey("dark_mode")] ?: false,
                    counter = prefs[intPreferencesKey("launch_count")] ?: 0
                )
            }
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(5000),
                initialValue = SettingsUiState()
            )
}

よくある質問

DataStoreはSharedPreferencesよりどのように優れていますか?

DataStoreは非同期で動作し(UIスレッドをブロックしません)、トランザクションによる同時アクセスをサポートし、Flowを介した変更へのリアクティブなサブスクリプションを可能にします。SharedPreferencesは同期APIであり、大量のデータでANRのリスクがあり、組み込みのリアクティブサポートはありません。

DataStoreはJavaで使用できますか?

DataStoreはKotlinで書かれており、Kotlin Coroutinesが必要です。Javaから使用することは可能ですが不便です:CompletableFutureでラッパーを作成するか、手動でコルーチンを管理する必要があります。Javaプロジェクトの場合、GoogleはSharedPreferencesを維持するか、モジュールにKotlinを追加することを推奨しています。

DataStoreは大量のデータ保存に適していますか?

DataStoreは読み取り時にファイル全体をメモリにロードするため、リストや大きなオブジェクトの保存には適していません。そのようなシナリオでは、RoomやSQLiteを使用してください。DataStoreは設定や小さな構造化データ(最大数百KB)に最適化されています。

破損したDataStoreファイルのエラーを処理するには?

DataStoreを作成する際に、corruptionHandlerを渡すことができます — ファイルが破損したときに呼び出される関数です。デフォルトでは、DataStoreはCorruptionExceptionをスローします。corruptionHandlerで空のデータを返すと、DataStoreはファイルを正しい状態で上書きします。

Proto DataStoreには.protoファイルが必須ですか?

はい、Proto DataStoreは.protoファイルでのスキーマ定義とprotobuf-gradle-pluginの接続が必要です。プロジェクトが小さくデータが単純な場合は、Preferences DataStoreを使用する方が簡単です — 追加のビルド設定は必要ありません。

まとめ

  • DataStore — Kotlin CoroutinesとFlowで動作するSharedPreferencesの最新の置き換え
  • Preferences DataStore — スキーマ不要のシンプルなキー値、設定に最適
  • Proto DataStore — protobufスキーマと自動マイグレーションを備えた型付きストレージ
  • マイグレーションはSharedPreferencesMigrationを介してDataStoreに組み込まれています
  • スレッドセーフ — すべての操作は非同期で、UIのブロッキングは排除されています
  • リアクティビティ — Flowはデータ変更のたびにサブスクライバーに通知します
  • 推奨 — すべての新しいAndroidプロジェクトでDataStoreを使用し、設定を扱う既存プロジェクトを移行してください

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください