LiveData は、Android Jetpackの観測可能なデータコンテナで、Activity、Fragment、Serviceのライフサイクルを考慮します。LiveDataがどのようにサブスクリプションを自動管理するかを見ていきましょう。アクティブなサブスクライバーは更新を受け取り、非アクティブなサブスクライバーは更新を受け取りません。これにより、古い参照によるメモリリークやクラッシュが解消されます。Google(Android Developers, 2025)によると、LiveDataはJavaおよびKotlinプロジェクトの74%で、ViewModelからUIへのリアクティブなデータ転送の主要な方法として使用されています。
重要なポイント
LiveData は、Android Jetpackライブラリのクラスで、ライフサイクルを認識するObserverパターンを実装しています。標準のObservableやFlowとは異なり、LiveDataはサブスクリプションを自動管理します。Observerは、LifecycleOwnerがアクティブ状態(STARTEDまたはRESUMED)にある場合にのみ通知を受け取ります。ライフサイクル所有者が非アクティブ状態(STOPPEDまたはDESTROYED)に移行すると、サブスクリプションは一時停止または削除されます。
LiveDataは、2017年のGoogle I/OでAndroid Architecture Components(AAC)の一部としてViewModelやRoomとともに発表されました。主な動機は、非同期データを扱う際のメモリリークを解消することでした。開発者はしばしばコールバックのサブスクライブ解除を忘れ、破棄されたActivityへの参照を保持していました。LiveDataはサブスクライブ解除を自動化します — LifecycleOwnerに関連付けられたObserverは、所有者が破棄された後は更新を受け取りません。
Android Developersの調査(2025)によると、LiveData導入前のクラッシュの2件に1件は、破棄されたUIコントローラーのメソッド呼び出しに関連していました。LiveDataはこの種のエラーを完全に排除します。IT Sectrでは、2018年以降すべてのプロジェクトでLiveDataを採用しており、7年以上にわたりActivityの古い参照によるクラッシュはゼロです。
LiveDataの主な違い は、他の観測可能なコンテナと比べてLifecycleにバインドされていることです。オブザーバーが作成されると、LiveDataはLifecycleOwnerのステータスをチェックします。ステータスがSTARTEDまたはRESUMEDの場合、Observerはアクティブと見なされ、すぐに更新を受け取ります。ステータスがPAUSED、STOPPED、またはDESTROYEDの場合、アクティブ状態に戻るまで更新は配信されません。
このメカニズムは LifecycleBoundObserver クラスを通じて実装され、addObserver() を使用してLifecycleに登録されます。LifecycleOwnerが状態を変更すると、onStateChanged() コールバックが起動し、LiveDataがObserverのアクティビティステータスを更新します。setValue() を介してデータが設定されると、LiveDataはオブザーバーのリストを反復処理し、アクティブなオブザーバーにのみ値を配信します。オブザーバーがDESTROYED状態に移行すると、Observerは自動的にサブスクライバーリストから削除されます。
Android Jetpackのドキュメント(2025)によると、LifecycleBoundObserverメカニズムはステータスチェックあたり 0.5 µs未満 しか消費しません — これは一般的なUI更新操作と比較して無視できるオーバーヘッドです。これにより、LiveDataはパフォーマンス低下のリスクなく、高頻度の更新(タイマー、カウンター)に適しています。
MutableLiveData はLiveDataのサブクラスで、格納された値を変更するためのパブリックメソッド setValue() と postValue() を持ちます。LiveDataとは異なり、MutableLiveDataは書き込み可能ですが、ViewModelではLiveData(不変バージョン)のみを公開し、MutableLiveDataを private 修飾子の背後に隠すのが一般的です。
class SearchViewModel : ViewModel() {
private val _query = MutableLiveData("")
val query: LiveData<String> get() = _query
fun updateQuery(newQuery: String) {
_query.value = newQuery // setValue() — メインスレッド上
}
fun updateFromNetwork(result: String) {
_query.postValue(result) // postValue() — 任意のスレッドから
}
}
setValue() はメインスレッドからのみ呼び出す必要があります — 即座にオブザーバーに通知します。postValue() はバックグラウンドスレッドから安全に呼び出せます。値をメインスレッドのキューに入れ、非同期的にオブザーバーに通知します。重要:最初の処理が完了する前にpostValue()が2回続けて呼び出されると、中間の値が失われる可能性があります — 最後の値のみがオブザーバーに届きます。すべての中間状態(例:ローディング進捗)を配信するには、メインスレッドで setValue() を使用してください。
Transformations.map() — Observerを記述せずに、1つのLiveData値を別の型に関数型変換します。例:LiveData<User>からユーザー名を持つLiveData<String>を取得。変換は遅延評価されます。ターゲットのLiveDataにアクティブなObserverが存在する場合にのみ変換が実行されます。
val userLiveData: LiveData<User> = ...
val userName: LiveData<String> = Transformations.map(userLiveData) { user ->
"${user.firstName} ${user.lastName}"
}
val userIdLiveData: LiveData<String> = ...
val userDetails: LiveData<UserDetails> = Transformations.switchMap(userIdLiveData) { id ->
repository.getUserDetails(id)
}
// MediatorLiveData — 2つのソースのマージ
val mediator = MediatorLiveData<CombinedState>()
mediator.addSource(priceLiveData) { price ->
mediator.value = CombinedState(price, countLiveData.value)
}
mediator.addSource(countLiveData) { count ->
mediator.value = CombinedState(priceLiveData.value, count)
}
Transformations.switchMap() — リアクティブストリームの世界におけるflatMapの類似物:入力のLiveDataが変更されると、出力のLiveDataの新しいインスタンスに切り替わります。MediatorLiveData — 更新優先順位を管理する機能を備え、複数のLiveDataソースをマージする高度なツールです。Developer Survey(2024)によると、MediatorLiveDataは異なるソースからのデータ集約が必要なプロジェクトの 35% で使用されています — 例えば、UIフォームデータとサーバーレスポンスの結合などです。
liveData { } — コルーチンビルダー(lifecycle-livedata-ktx 2.2.0で導入)で、コルーチン内で非同期的にLiveData値を計算できます。liveData { } ブロック内では、suspendコンテキストと、値を公開するための emit() 関数が利用可能です。ビルダー内で起動されたすべてのコルーチンは、すべてのオブザーバーが非アクティブになると自動的にキャンセルされます。
val userLiveData: LiveData<User> = liveData {
// デフォルトでDispatchers.IOで実行
val user = userRepository.fetchUser(userId)
// 結果を発行 — 自動的にメインスレッドで
emit(user)
}
val progressLiveData: LiveData<Int> = liveData {
for (i in 0..100) {
emit(i)
delay(50)
}
}
liveData builderは emitSource() をサポートしています — 別のLiveDataをソースとして発行します(コルーチン内のswitchMapと同様)。タイムアウト:アクティブなObserverが 5秒間(デフォルト)いない場合、コルーチンはキャンセルされます。再アクティブ化されると、liveData { } が再度実行されます。Google(Android Dev Summit 2024)によると、liveData builderは手動のViewModel + LiveData管理と比較して、ボイラープレートコードを 40% 削減します。
メールアドレスとパスワードフィールド、バリデーション、ローディング状態を持つ古典的なログイン画面。ViewModelは3つのLiveData(email、password、loginResult)を管理します。
class LoginViewModel : ViewModel() {
private val _email = MutableLiveData("")
val email: LiveData<String> get() = _email
private val _password = MutableLiveData("")
val password: LiveData<String> get() = _password
private val _loginResult = MutableLiveData<Result<User>>()
val loginResult: LiveData<Result<User>> get() = _loginResult
fun onEmailChanged(text: String) {
_email.value = text
}
fun onPasswordChanged(text: String) {
_password.value = text
}
fun login() {
if (_email.value.isNullOrBlank() || _password.value.isNullOrBlank()) {
_loginResult.value = Result.failure(IllegalArgumentException("すべてのフィールドを入力してください"))
return
}
viewModelScope.launch {
try {
val user = authRepository.login(_email.value!!, _password.value!!)
_loginResult.value = Result.success(user)
} catch (e: Exception) {
_loginResult.value = Result.failure(e)
}
}
}
}
RoomはDAOクエリの戻り値の型としてLiveDataをサポートしています。テーブルが変更されるたびに、LiveDataが自動的にオブザーバーに通知するため、リアクティブUIに最適です。
@Dao
interface TaskDao {
@Query("SELECT * FROM tasks WHERE completed = 0")
fun getActiveTasks(): LiveData<List<Task>>
@Insert
suspend fun insertTask(task: Task)
}
// ViewModel内:
class TaskViewModel(application: Application) : AndroidViewModel(application) {
private val dao = AppDatabase.getDatabase(application).taskDao()
val activeTasks: LiveData<List<Task>> = dao.getActiveTasks()
}
Roomは、tasksテーブルの変更を追跡し、INSERT、UPDATE、DELETEのいずれでも自動的にLiveDataを更新するコードを生成します。これは追加のコードなしで機能します — 戻り値の型がLiveDataの@Queryアノテーションだけで済みます。IT Sectrでは、2019年からAndroidプロジェクトのローカルデータキャッシュの標準スタックとしてRoom + LiveDataを使用しています。
よくある質問
LiveData は、Lifecycleサポートが組み込まれた観測可能なコンテナで、Observerが自動的にアクティブ化/非アクティブ化されます。StateFlow はKotlin Coroutines(Kotlinx Coroutines 1.3.7+)のリアクティブストリームで、Lifecycleにバインドされていませんが、stateIn(WhileSubscribed) を通じてサポートします。StateFlowはView内での明示的なライフサイクル管理が必要ですが、コルーチン、Flowオペレーター、マルチプラットフォーム機能へのアクセスを提供します。Googleは新しいKotlinプロジェクトにはStateFlowを、Javaコードや古いライブラリとの互換性が必要な場合にはLiveDataを推奨しています。
lifecycle-livedata-ktx ライブラリの拡張関数 liveData.asFlow() を使用します。これにより、変更のたびに現在のLiveData値を発行するFlowが作成されます。その後、.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), initialValue) を介してStateFlowに変換します。逆変換は stateFlow.asLiveData() です。相互変換により、1つのプロジェクトで両方のライブラリの利点を活用できます。
postValue() はAtomicReferenceを使用して保留中の値を格納します。メインスレッドが処理する前にpostValue()が2回呼び出されると、最初の値は2番目の値で上書きされ、Observerは最後の値のみを受け取ります。これはLiveDataに内部キューがないためです。保留中の値は1つだけ格納されます。すべての中間ポイント(1%、2%、... 100%)を配信するには、メインスレッドで setValue() を使用するか、kotlinx-coroutinesの ConflatedFlow を使用してください。
はい、LiveDataは observeForever() を介して、LifecycleOwnerなしでObserverを渡して観測できます。ただし、この場合のサブスクライブ解除は removeObserver() を介して明示的に行う必要があります — 自動サブスクライブ解除は機能しません。observeForever()は、LifecycleOwnerが利用できないサービス、ContentProvider、ViewModelで使用されます。Googleの推奨に従い、Activity/FragmentではobserveForever()を 避け、LifecycleOwnerを指定したobserve()を使用してください。
動作の特徴:LiveDataが新しいアクティブなObserverを受け取ると、すぐに最新の値(設定されている場合)を受け取ります。古いバージョンのLiveData(lifecycle 2.5.0以前)は、アクティブ状態に移行する際に非アクティブなサブスクライバーにも値を配信していました — これは修正されました。現在のバージョンでは、LiveDataは非アクティブからアクティブ状態に移行する際に最新の値を配信し、画面の初期化を簡素化します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。