KotlinのViewModelについて

採用はこちら

ViewModel は、Android のモダンアーキテクチャにおいて UI とロジックを分離するための中核コンポーネントです。

特に MVVM(Model–View–ViewModel)構成では、設計の良し悪しを左右する非常に重要な存在となります。

本記事では、ViewModel の役割・ライフサイクル・正しい使い方・注意点を、誤解が生じやすい点を修正しながら解説します。

目次

ViewModelとは何か

ViewModel は、UI に表示される状態(State)と、それを生成・更新するロジックを保持するクラスです。

主な目的は次の3点です。

  • Activity / Fragment からロジックを分離する
  • 構成変更(画面回転など)時に UI 状態を保持する
  • UI に依存しない形でロジックを管理し、テストしやすくする

ViewModel 自体は View(Activity / Fragment)を知らない という点が重要です。

なぜ ViewModel が必要なのか

Activity / Fragment に直接ロジックを書く場合の問題点

  • 画面回転などの構成変更で onCreate() が再実行され、状態が失われやすい
  • UI とビジネスロジックが密結合になり、クラスが肥大化する
  • テストが困難になる
  • 再利用性が低い

ViewModel は、これらの問題を解決するために導入されました。

MVVM における ViewModel の役割

MVVM では、責務を以下のように分離します。

  • View(Activity / Fragment)
    表示とユーザー操作のみを担当
  • ViewModel
    UI 状態の管理とロジックを担当
  • Model(Repository / UseCase 等)
    データ取得や変換を担当

重要なのは依存関係の向きです。

  • View → ViewModel を参照する
  • ViewModel は View を一切参照しない

この一方向の依存が、保守性の高い設計を実現します。

ViewModel の基本的な構造(Kotlin)

class MainViewModel : ViewModel() {

    private val _count = MutableLiveData(0)
    val count: LiveData<Int> = _count

    fun increment() {
        _count.value = (_count.value ?: 0) + 1
    }
}

ポイント

  • MutableLiveData は ViewModel 内部に隠す
  • View には LiveData として公開する
  • UI 状態の更新責務は ViewModel が持つ

このカプセル化は必須です。

View から ViewModel を使う方法

Activity の場合

class MainActivity : AppCompatActivity() {

    private val viewModel: MainViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        viewModel.count.observe(this) { value ->
            textView.text = value.toString()
        }
    }
}

Fragment の場合

private val viewModel: MainViewModel by viewModels()

なお、activityViewModels() を使えば、複数 Fragment で同じ ViewModel を共有することも可能です。

この場合、ViewModel は Activity スコープで管理されます。

ViewModel のライフサイクル

ViewModel は ViewModelStoreOwner(Activity / Fragment / Navigation スコープなど)に紐づいて管理されます。

構成変更(画面回転など)の場合

  • Activity / Fragment は破棄され、新しいインスタンスが生成される
  • ただし ViewModelStore は保持される
  • 同じスコープであれば、既存の ViewModel が再利用される

このため、画面回転時でも状態を保持できます。

完全にスコープが破棄された場合

  • Activity が終了する
  • Navigation のバックスタックから完全に外れる

このタイミングで ViewModel は破棄されます。

「Activity が destroy されたら必ず ViewModel も破棄される」というわけではなく、スコープが継続しているかどうかが判断基準になります。

非同期処理と ViewModel

ViewModel は、構成変更中でも生き続けるため、非同期処理の置き場所として適しています

ただし重要なのは前提条件です。

  • 構成変更で ViewModel が保持される場合
    → 非同期処理は継続できる
  • スコープが終了して ViewModel が破棄された場合
    → 処理は終了させる必要がある

viewModelScope と Coroutine

class MainViewModel : ViewModel() {

    fun loadData() {
        viewModelScope.launch {
            val result = repository.fetchData()
            _data.value = result
        }
    }
}

viewModelScope の特徴

  • ViewModel のライフサイクルに紐づく CoroutineScope
  • ViewModel がクリアされると自動でキャンセルされる
  • メモリリークや不要な処理継続を防げる

ViewModel で Coroutine を使う場合、原則 viewModelScope を使用します。

LiveData と StateFlow の位置づけ

LiveData

  • Android ライフサイクルに強く依存
  • observe 時に自動でライフサイクル制御される
  • XML + DataBinding と相性が良い

StateFlow

  • Kotlin Coroutine 標準
  • Flow ベースで柔軟性が高い
  • Jetpack Compose と相性が良い
  • ライフサイクル制御は自分で行う必要がある(repeatOnLifecycle 等)

実務的な指針

  • Compose / Flow 中心 → StateFlow
  • XML / 既存コード重視 → LiveData

どちらが「絶対的に正しい」わけではなく、UI層の設計に合わせて選択します。

ViewModel に書いてよいもの・避けるべきもの

書いてよいもの

  • UI 状態(数値、リスト、ロード状態など)
  • 入力検証ロジック
  • ビジネスロジック
  • Repository 呼び出し
  • 非同期処理制御

避けるべきもの

  • Activity / Fragment / View の参照
  • Toast 表示や画面遷移などの UI 操作
  • findViewById などの View 操作
  • Context(特に Activity Context)

※ 例外として、Application Context が必要な場合は AndroidViewModel を使う選択肢がありますが、乱用は避けるべきです。

UIイベント(Toast・Navigation)の扱い

Toast や画面遷移は 状態ではなくイベントです。

単純に LiveData<String> で通知すると、

  • 再購読時(回転など)に同じイベントが再実行される

という問題が起こります。

そのため、

  • SharedFlow
  • Channel
  • one-shot event パターン

などを使い、「一度きりの通知」として扱う設計が推奨されます。

ViewModel は「UI スコープ単位」で考える

ViewModel は Repository のように アプリ全体で使い回すものではありません

  • ViewModel → UI スコープ単位(画面、Activity、NavGraph など)
  • Repository → データ単位(API、DB、キャッシュなど)

この責務分離を守ることで、設計は大幅に安定します。

まとめ

  • ViewModel は UI 状態とロジックを管理するためのクラス
  • 構成変更でもスコープが生きていれば状態を保持できる
  • 非同期処理は viewModelScope で管理する
  • View や Context を直接扱わない
  • 状態とイベントを明確に分離する
  • UI スコープ単位で ViewModel を設計する

以上、KotlinのViewModelについてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次