DataStore移行でハマるポイント|SharedPreferences置き換え手順

スポンサーリンク

導入:DataStoreに移行したい。でも怖い

Android開発を続けていると、だいたいこのタイミングが来ます。

「SharedPreferences、そろそろDataStoreにした方がいいよね」
「でも、既存データ消えたらどうしよう…」

これ、めちゃくちゃ分かります。
結論から言うと、正しい手順を踏めばDataStore移行は怖くありません

この記事では、SharedPreferencesからDataStoreへ安全に置き換える手順と、
実務でハマりがちなポイントをまとめます。

先に結論です。

  • いきなり全置き換えはしない
  • 既存データの読み込み経路を確保する
  • Repository層で段階的に切り替える

前提整理:SharedPreferencesとDataStoreの違い

移行の話をする前に、まず思想の違いを押さえておきます。

  • SharedPreferences:同期APIあり・シンプルだが事故りやすい
  • DataStore:非同期・スレッド安全・Flow前提

この違いを理解していないと、
「DataStoreにしたのに逆に複雑になった」現象が起きます。

基本的な違いについては、以下の記事で詳しく解説しています。


SharedPreferencesとDataStoreの違いと正しい使い分け

なぜDataStore移行でハマるのか

多くの失敗は、「APIの置き換え」だと思ってしまうことが原因です。

よくある勘違い

  • getString → dataStore.data.map に置き換えればOK
  • SharedPreferencesを消しても問題ない
  • 同期処理のまま書き換えられる

DataStoreは非同期・Flow前提です。
設計ごと変えないと、確実に詰まります。

安全な移行の全体像

おすすめの移行手順は、以下の流れです。

  • ① DataStoreを新規追加する
  • ② SharedPreferencesを「読み取り専用」にする
  • ③ DataStoreへデータをコピーする
  • ④ DataStoreのみ参照する
  • ⑤ SharedPreferencesを削除する

段階的に切り替えるのが最大のポイントです。

ステップ① DataStoreを追加する


val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
    name = "app_datastore"
)

この時点では、
まだSharedPreferencesは消しません。

ステップ② SharedPreferencesを読み取り専用にする

まずは「書き込み」を止めます。


class LegacyPref(private val context: Context) {

    private val prefs =
        context.getSharedPreferences("legacy_prefs", Context.MODE_PRIVATE)

    fun getUserId(): String? {
        return prefs.getString("user_id", null)
    }
}

ここで新規保存を止めることで、
データの二重管理を防げます。

ステップ③ DataStoreへコピーする

初回起動時などで、一度だけコピーします。


suspend fun migrateIfNeeded() {
    val legacyUserId = legacyPref.getUserId() ?: return

    context.dataStore.edit { prefs ->
        prefs[stringPreferencesKey("user_id")] = legacyUserId
    }
}

ここで重要なのは、
コピー完了後に二度とSharedPreferencesを見ないことです。

ステップ④ Repository層で吸収する

UIやUseCaseから、
直接DataStoreやSharedPreferencesを触らせないようにします。


class UserRepository(
    private val dataStore: DataStore<Preferences>
) {
    val userIdFlow: Flow<String?> =
        dataStore.data.map { it[stringPreferencesKey("user_id")] }
}

こうすることで、
保存方式を変えても影響範囲が限定されます。

実務でよくあるハマりポイント

① 同期処理の感覚が抜けない

DataStoreは基本 suspend / Flow です。
getString感覚で使うと破綻します。

② UI層でcollectしっぱなし

ライフサイクルを考えないcollectは、
メモリリークの温床です。

③ すべてnullableになる

Repositoryで初期値を補正しないと、
UIが nullable 地獄になります。

テスト・確認観点

  • 既存ユーザーのデータが保持されているか
  • 初回起動時のコピーが一度だけ行われるか
  • DataStore未初期化時の挙動
  • アプリ再起動後の値

よくある質問

Q. Proto DataStoreじゃダメ?

問題ありません。ただし移行コストは上がります。
まずはPreferences DataStoreで十分なケースが多いです。

Q. SharedPreferencesはすぐ消していい?

NGです。
移行完了・テスト完了後に削除しましょう。

まとめ

  • DataStore移行は「設計変更」
  • 段階的移行が安全
  • Repositoryで吸収するのが正解

迷ったら:SharedPreferencesをいきなり消さない。
これだけ守れば、大事故は防げます。

コメント

タイトルとURLをコピーしました