LiveData の概要 Android Jetpack の一部
LiveData
は監視可能なデータホルダー クラスです。通常の監視とは異なり、LiveData はライフサイクルに応じた監視が可能です。つまり、アクティビティ、フラグメント、サービスなどの他のアプリ コンポーネントのライフサイクルが考慮されます。このため、LiveData はライフサイクルの状態がアクティブなアプリ コンポーネント オブザーバーのみを更新します。
オブザーバー(Observer
クラスで表されます)のライフサイクルの状態が STARTED
または RESUMED
の場合、LiveData はそのオブザーバーをアクティブな状態であると見なします。LiveData は更新に関する情報をアクティブなオブザーバーにのみ通知します。LiveData
オブジェクトを監視するために登録されている非アクティブなオブザーバーには、変更に関する通知は行われません。
オブザーバーは、LifecycleOwner
インターフェースを実装するオブジェクトとペアで登録できます。このようにペアリングすることで、対応する Lifecycle
オブジェクトの状態が DESTROYED
に変わったときにオブザーバーを削除できます。
特に、アクティビティとフラグメントでこの手法を利用すると、LiveData
オブジェクトを安全に監視でき、リークについて心配する必要がなくなるため非常に有用です。アクティビティとフラグメントは、そのライフサイクルが破棄されるとすぐに登録解除されます。
LiveData の使用方法について詳しくは、LiveData オブジェクトを操作するをご覧ください。
LiveData を使用するメリット
LiveData を使用するメリットには次のようなものがあります。
- UI をデータの状態と一致させることができる
- LiveData はオブザーバーのパターンに従います。LiveData は、基盤となるデータが変更されると
Observer
オブジェクトに通知します。Observer
オブジェクト内の UI を更新するコードは統合できます。これにより、オブザーバーが自動的に更新を行うので、アプリデータが変更されるたびに UI を更新する必要がなくなります。 - メモリリークが発生しない
- オブザーバーは
Lifecycle
オブジェクトにバインドしていて、関連付けられているライフサイクルが破棄されたときに自身のクリーンアップを行います。 - 停止されたアクティビティに起因するクラッシュが発生しない
- オブザーバーのライフサイクルがアクティブでない場合(アクティビティがバックスタック内にある場合など)、オブザーバーは LiveData イベントを受け取りません。
- 手動によるライフサイクル処理が不要
- UI コンポーネントは関連データを監視するだけで、監視の停止や再開は行いませんが、LiveData はそのすべてを自動的に管理します。これは、LiveData が監視を行いながら、関連するライフサイクルの状態の変化を認識するためです。
- データが常に最新
- ライフサイクルが非アクティブになると、各コンポーネントは再びアクティブになったときに最新のデータを受け取ります。たとえば、バックグラウンドのアクティビティは、フォアグラウンドに戻った直後に最新のデータを受け取ります。
- 適切な設定の変更
- アクティビティやフラグメントは、設定の変更(デバイスの回転など)によって再作成されると、使用可能な最新のデータをすぐに受け取ります。
- リソースの共有
- シングルトン パターンを使用してシステム サービスをラップすることで、
LiveData
オブジェクトを拡張できます。これにより、アプリ内でリソースを共有できるようになります。LiveData
オブジェクトがシステム サービスに関連付けられると、リソースを必要とするすべてのオブザーバーがLiveData
オブジェクトを監視できるようになります。詳しくは、LiveData を拡張するをご覧ください。
LiveData オブジェクトを操作する
LiveData
オブジェクトを操作する手順は次のとおりです。
- 特定のタイプのデータを保持する
LiveData
のインスタンスを作成します。これは通常、ViewModel
クラス内で行います。 onChanged()
メソッドを定義するObserver
オブジェクトを作成します。このオブジェクトは、LiveData
オブジェクトが保持するデータが変更されたときの処理を管理します。通常は、UI コントローラ(アクティビティやフラグメントなど)内にObserver
オブジェクトを作成します。observe()
メソッドを使用して、Observer
オブジェクトをLiveData
オブジェクトにアタッチします。observe()
メソッドはLifecycleOwner
オブジェクトを取得します。これにより、Observer
オブジェクトがLiveData
オブジェクトに登録され、変更が通知されるようになります。通常は、UI コントローラ(アクティビティやフラグメントなど)内のObserver
オブジェクトをアタッチします。
LiveData
オブジェクトに格納されている値を更新する場合、アタッチされている LifecycleOwner
がアクティブな状態である限り、登録済みのすべてのオブザーバーがトリガーされます。
LiveData を使用すると、UI コントローラのオブザーバーが最新情報を受け取ることができます。LiveData
オブジェクトで保持されているデータが変更されると、それに応じて UI が自動的に更新されます。
LiveData オブジェクトを作成する
LiveData は、任意のデータ(List
などの Collections
を実装するオブジェクトを含む)とともに使用できるラッパーです。通常、LiveData
オブジェクトは ViewModel
オブジェクトに格納され、次の例に示すように getter メソッドを介してアクセスします。
Kotlin
class NameViewModel : ViewModel() { // Create a LiveData with a String val currentName: MutableLiveData<String> by lazy { MutableLiveData<String>() } // Rest of the ViewModel... }
Java
public class NameViewModel extends ViewModel { // Create a LiveData with a String private MutableLiveData<String> currentName; public MutableLiveData<String> getCurrentName() { if (currentName == null) { currentName = new MutableLiveData<String>(); } return currentName; } // Rest of the ViewModel... }
最初の段階では、LiveData
オブジェクトのデータは設定されていません。
ViewModel
クラスのメリットと使用方法について詳しくは、ViewModel ガイドをご覧ください。
LiveData オブジェクトを監視する
ほとんどの場合、アプリ コンポーネントの onCreate()
メソッドが LiveData
オブジェクトの監視を開始するのに適した場所になっています。理由は以下のとおりです。
- アクティビティやフラグメントの
onResume()
メソッドからの冗長な呼び出しをシステムが行わないようにすることができるため。 - アクティビティやフラグメントがアクティブになり次第、そのデータを表示できるため。アプリ コンポーネントは
STARTED
の状態になるとすぐに、監視対象のLiveData
オブジェクトから最新の値を受け取ります。これは、監視対象のLiveData
オブジェクトが設定されている場合にのみ行われます。
LiveData は通常、データが変更された場合にのみ最新データを配信します(対象はアクティブなオブザーバーのみ)。オブザーバーは非アクティブな状態からアクティブな状態に変わったときにも最新データを受け取りますが、これは例外的な動作です。さらに、オブザーバーが次に非アクティブな状態からアクティブな状態に変わったときには、前回アクティブになったときから値が変更された場合にのみ最新データを受け取ります。
次のサンプルコードは、LiveData
オブジェクトの監視を開始する方法を示しています。
Kotlin
class NameActivity : AppCompatActivity() { // Use the 'by viewModels()' Kotlin property delegate // from the activity-ktx artifact private val model: NameViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Other code to setup the activity... // Create the observer which updates the UI. val nameObserver = Observer<String> { newName -> // Update the UI, in this case, a TextView. nameTextView.text = newName } // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer. model.currentName.observe(this, nameObserver) } }
Java
public class NameActivity extends AppCompatActivity { private NameViewModel model; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Other code to setup the activity... // Get the ViewModel. model = new ViewModelProvider(this).get(NameViewModel.class); // Create the observer which updates the UI. final Observer<String> nameObserver = new Observer<String>() { @Override public void onChanged(@Nullable final String newName) { // Update the UI, in this case, a TextView. nameTextView.setText(newName); } }; // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer. model.getCurrentName().observe(this, nameObserver); } }
observe()
が呼び出され、パラメータとして nameObserver
が渡されると、onChanged()
がすぐに呼び出され、mCurrentName
に格納されている最新の値が提供されます。LiveData
オブジェクトの mCurrentName
に値が設定されていない場合、onChanged()
は呼び出されません。
LiveData オブジェクトを更新する
LiveData には、格納されているデータを更新するための公開メソッドはありません。LiveData
オブジェクトに格納されている値を編集する必要がある場合は、MutableLiveData
クラスで公開されている setValue(T)
メソッドと postValue(T)
メソッドを使用する必要があります。通常、MutableLiveData
は ViewModel
で使用され、その場合、ViewModel
は不変の LiveData
オブジェクトのみをオブザーバーに公開します。
オブザーバーの関連付けをセットアップすると、次の例に示すように、LiveData
オブジェクトの値を更新できるようになります。これにより、ユーザーがボタンをタップしたときにすべてのオブザーバーがトリガーされます。
Kotlin
button.setOnClickListener { val anotherName = "John Doe" model.currentName.setValue(anotherName) }
Java
button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String anotherName = "John Doe"; model.getCurrentName().setValue(anotherName); } });
この例の setValue(T)
を呼び出すと、値 John Doe
が指定された onChanged()
メソッドがオブザーバーによって呼び出されます。これはボタン押下の例ですが、ネットワーク リクエストに対する応答や、データベースの読み込み完了など、さまざまな理由で setValue()
または postValue()
を呼び出して mName
を更新できます。いずれの場合でも、setValue()
または postValue()
を呼び出すことによってオブザーバーがトリガーされ、UI が更新されます。
LiveData と Room を併用する
Room 永続ライブラリは、LiveData
オブジェクトを返す監視可能なクエリをサポートしています。監視可能なクエリは、データベース アクセス オブジェクト(DAO)の一部として記述されます。
データベースが更新されると、LiveData
オブジェクトの更新に必要なすべてのコードが Room によって生成されます。生成されたコードにより、クエリが必要に応じてバックグラウンドのスレッドで非同期実行されます。このパターンは、データベースに格納されているデータと同期しているデータを UI に表示し続けるのに役立ちます。Room と DAO について詳しくは、Room 永続ライブラリ ガイドをご覧ください。
LiveData でコルーチンを使用する
LiveData
には、Kotlin コルーチンのサポートが含まれています。詳しくは、Android アーキテクチャ コンポーネントで Kotlin コルーチンを使用するをご覧ください。
アプリのアーキテクチャの LiveData
LiveData
はライフサイクル対応であり、アクティビティやフラグメントなどのエンティティのライフサイクルに従います。LiveData
を使用して、これらのライフサイクル オーナーと、存続期間の異なる他のオブジェクト(ViewModel
オブジェクトなど)の間で通信します。ViewModel
の主な役割は UI 関連のデータの読み込みと管理であり、LiveData
オブジェクトの保持に適しています。ViewModel
の LiveData
オブジェクトを作成し、これを使用して状態を UI レイヤに公開します。
アクティビティとフラグメントの役割は、状態の保持ではなくデータの表示であるため、LiveData
インスタンスを保持する必要はありません。また、アクティビティとフラグメントがデータを保持しないようにすることで、単体テストの作成が容易になります。
データレイヤ クラスの LiveData
オブジェクトを利用しようとしても、LiveData
は非同期データ ストリームを処理するようには設計されていません。これは LiveData
変換と MediatorLiveData
を使用して実現できますが、このアプローチには欠点があります。データ ストリームを組み合わせる機能が非常に限られており、LiveData
オブジェクト(変換によって作成されたものを含む)はすべて、メインスレッドで監視されます。次のコードは、LiveData
を Repository
に保持することでメインスレッドをブロックする方法の例を示しています。
Kotlin
class UserRepository { // DON'T DO THIS! LiveData objects should not live in the repository. fun getUsers(): LiveData<List<User>> { ... } fun getNewPremiumUsers(): LiveData<List<User>> { return getUsers().map { users -> // This is an expensive call being made on the main thread and may // cause noticeable jank in the UI! users .filter { user -> user.isPremium } .filter { user -> val lastSyncedTime = dao.getLastSyncedTime() user.timeCreated > lastSyncedTime } } }
Java
class UserRepository { // DON'T DO THIS! LiveData objects should not live in the repository. LiveData<List<User>> getUsers() { ... } LiveData<List<User>> getNewPremiumUsers() { return Transformations.map(getUsers(), // This is an expensive call being made on the main thread and may cause // noticeable jank in the UI! users -> users.stream() .filter(User::isPremium) .filter(user -> user.getTimeCreated() > dao.getLastSyncedTime()) .collect(Collectors.toList())); } }
アプリの他のレイヤでデータ ストリームを使用する必要がある場合は、Kotlin Flows を使用し、asLiveData()
を使用して ViewModel
の LiveData
に変換することをご検討ください。LiveData
で Kotlin Flow
を使用する方法について詳しくは、こちらの Codelab をご覧ください。Java で構築されたコードベースの場合は、コールバックまたは RxJava
と組み合わせて Executor を使用することをご検討ください。
LiveData を拡張する
オブザーバーのライフサイクルの状態が STARTED
または RESUMED
の場合、LiveData はオブザーバーがアクティブな状態であると見なします。次のサンプルコードは、LiveData
クラスを拡張する方法を示しています。
Kotlin
class StockLiveData(symbol: String) : LiveData<BigDecimal>() { private val stockManager = StockManager(symbol) private val listener = { price: BigDecimal -> value = price } override fun onActive() { stockManager.requestPriceUpdates(listener) } override fun onInactive() { stockManager.removeUpdates(listener) } }
Java
public class StockLiveData extends LiveData<BigDecimal> { private StockManager stockManager; private SimplePriceListener listener = new SimplePriceListener() { @Override public void onPriceChanged(BigDecimal price) { setValue(price); } }; public StockLiveData(String symbol) { stockManager = new StockManager(symbol); } @Override protected void onActive() { stockManager.requestPriceUpdates(listener); } @Override protected void onInactive() { stockManager.removeUpdates(listener); } }
この例の価格リスナーの実装には、以下の重要なメソッドが含まれています。
onActive()
メソッドは、LiveData
オブジェクトにアクティブなオブザーバーが含まれている場合に呼び出されます。つまり、株価の最新情報の監視はこのメソッドから開始する必要があります。onInactive()
メソッドは、LiveData
オブジェクトにアクティブなオブザーバーが含まれていない場合に呼び出されます。 リッスンしているオブザーバーがないため、StockManager
サービスへの接続を維持する理由はありません。setValue(T)
メソッドは、LiveData
インスタンスの値を更新し、変更に関する情報をアクティブなオブザーバーに通知します。
StockLiveData
クラスは次のように使用できます。
Kotlin
public class MyFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val myPriceListener: LiveData<BigDecimal> = ... myPriceListener.observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? -> // Update the UI. }) } }
Java
public class MyFragment extends Fragment { @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); LiveData<BigDecimal> myPriceListener = ...; myPriceListener.observe(getViewLifecycleOwner(), price -> { // Update the UI. }); } }
observe()
メソッドは、フラグメントのビューに関連付けられた LifecycleOwner
を最初の引数として渡します。こうすることで、このオブザーバーがオーナーに関連付けられている Lifecycle
オブジェクトにバインドされていることが示されます。つまり、以下のようになります。
Lifecycle
オブジェクトがアクティブな状態でない場合、値が変更されてもオブザーバーは呼び出されません。Lifecycle
オブジェクトが破棄された後、オブザーバーは自動的に削除されます。
LiveData
オブジェクトがライフサイクル対応だと、複数のアクティビティ、フラグメント、サービスの間でそれを共有できます。簡単な例として、次のように LiveData
クラスをシングルトンとして実装できます。
Kotlin
class StockLiveData(symbol: String) : LiveData<BigDecimal>() { private val stockManager: StockManager = StockManager(symbol) private val listener = { price: BigDecimal -> value = price } override fun onActive() { stockManager.requestPriceUpdates(listener) } override fun onInactive() { stockManager.removeUpdates(listener) } companion object { private lateinit var sInstance: StockLiveData @MainThread fun get(symbol: String): StockLiveData { sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol) return sInstance } } }
Java
public class StockLiveData extends LiveData<BigDecimal> { private static StockLiveData sInstance; private StockManager stockManager; private SimplePriceListener listener = new SimplePriceListener() { @Override public void onPriceChanged(BigDecimal price) { setValue(price); } }; @MainThread public static StockLiveData get(String symbol) { if (sInstance == null) { sInstance = new StockLiveData(symbol); } return sInstance; } private StockLiveData(String symbol) { stockManager = new StockManager(symbol); } @Override protected void onActive() { stockManager.requestPriceUpdates(listener); } @Override protected void onInactive() { stockManager.removeUpdates(listener); } }
また、次のようにフラグメント内で使用することもできます。
Kotlin
class MyFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) StockLiveData.get(symbol).observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? -> // Update the UI. }) }
Java
public class MyFragment extends Fragment { @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); StockLiveData.get(symbol).observe(getViewLifecycleOwner(), price -> { // Update the UI. }); } }
MyPriceListener
インスタンスは複数のフラグメントとアクティビティで監視できます。
そのうちの 1 つ以上が参照可能でアクティブな場合にのみ、LiveData はシステム サービスに接続します。
LiveData を変換する
LiveData
オブジェクトに格納されている値の変更は、オブザーバーにディスパッチする前に行うことをおすすめします。そうしないと、別のインスタンスの値に基づいて異なる LiveData
インスタンスを返さなければならなくなることがあります。Lifecycle
パッケージが提供する Transformations
クラスには、このようなシナリオをサポートするヘルパー メソッドが含まれています。
Transformations.map()
LiveData
オブジェクトに保存されている値に関数を適用し、結果を下流に伝えます。
Kotlin
val userLiveData: LiveData<User> = UserLiveData() val userName: LiveData<String> = userLiveData.map { user -> "${user.name} ${user.lastName}" }
Java
LiveData<User> userLiveData = ...; LiveData<String> userName = Transformations.map(userLiveData, user -> { user.name + " " + user.lastName });
Transformations.switchMap()
map()
と同様に、LiveData
オブジェクトに格納されている値に関数を適用し、結果のラップを解除して下流にディスパッチします。switchMap()
に渡された関数は、次の例に示すように、LiveData
オブジェクトを返す必要があります。
Kotlin
private fun getUser(id: String): LiveData<User> { ... } val userId: LiveData<String> = ... val user = userId.switchMap { id -> getUser(id) }
Java
private LiveData<User> getUser(String id) { ...; } LiveData<String> userId = ...; LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );
変換メソッドを使用すると、オブザーバーのライフサイクル全体にわたって情報を伝えることができます。返された LiveData
オブジェクトをオブザーバーが監視している場合を除き、変換は計算されません。変換は必要に応じて計算されるため、ライフサイクル関連の動作は暗黙的に伝えられます。追加の明示的な呼び出しや依存関係は必要ありません。
ViewModel
オブジェクト内に Lifecycle
オブジェクトが必要だと思われる場合、ソリューションとしてはおそらく変換が適しています。たとえば、住所を受け取ってその住所の郵便番号を返す UI コンポーネントがあるとします。このコンポーネントには、次のサンプルコードに示すように、単純な ViewModel
を実装できます。
Kotlin
class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() { private fun getPostalCode(address: String): LiveData<String> { // DON'T DO THIS return repository.getPostCode(address) } }
Java
class MyViewModel extends ViewModel { private final PostalCodeRepository repository; public MyViewModel(PostalCodeRepository repository) { this.repository = repository; } private LiveData<String> getPostalCode(String address) { // DON'T DO THIS return repository.getPostCode(address); } }
UI コンポーネントは、以前の LiveData
オブジェクトへの登録を解除して、getPostalCode()
を呼び出すたびに新しいインスタンスに登録する必要があります。また、UI コンポーネントを再作成する場合は、以前の呼び出しの結果を使用するのではなく、repository.getPostCode()
メソッドの呼び出しをもう一度トリガーします。
さらに、次の例に示すように、住所の入力の変換として郵便番号の検索を実装することもできます。
Kotlin
class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() { private val addressInput = MutableLiveData<String>() val postalCode: LiveData<String> = addressInput.switchMap { address -> repository.getPostCode(address) } private fun setInput(address: String) { addressInput.value = address } }
Java
class MyViewModel extends ViewModel { private final PostalCodeRepository repository; private final MutableLiveData<String> addressInput = new MutableLiveData(); public final LiveData<String> postalCode = Transformations.switchMap(addressInput, (address) -> { return repository.getPostCode(address); }); public MyViewModel(PostalCodeRepository repository) { this.repository = repository } private void setInput(String address) { addressInput.setValue(address); } }
この場合、postalCode
フィールドは addressInput
の変換として定義されます。アプリのアクティブなオブザーバーが postalCode
フィールドに関連付けられている場合、addressInput
が変更されるたびに、そのフィールドの値は再計算され、取得されます。
このメカニズムにより、必要に応じてオンデマンドで計算される LiveData
オブジェクトを下位レベルのアプリで作成できます。ViewModel
オブジェクトでは、LiveData
オブジェクトへの参照を簡単に取得でき、それを基盤として変換ルールを定義できます。
新しい変換を作成する
アプリで役に立つ可能性がある 12 個の特殊な変換が用意されています。ただし、それらの変換はデフォルトでは提供されていません。独自の変換を実装するには、MediatorLiveData
クラスを使用します。このクラスは他の LiveData
オブジェクトをリッスンし、そのオブジェクトによって生成されたイベントを処理します。MediatorLiveData
はその状態を元の LiveData
オブジェクトに正しく伝えます。このパターンについて詳しくは、Transformations
クラスのリファレンス ドキュメントをご覧ください。
複数の LiveData ソースを統合する
MediatorLiveData
は LiveData
のサブクラスです。このサブクラスを使用すると、複数の LiveData ソースを統合できます。元の LiveData ソース オブジェクトのいずれかが変更されると、MediatorLiveData
オブジェクトのオブザーバーが必ずトリガーされます。
たとえば、ローカル データベースやネットワークから更新可能な LiveData
オブジェクトが UI 内にある場合、以下のソースを MediatorLiveData
オブジェクトに追加できます。
- データベースに格納されているデータに関連付けられている
LiveData
オブジェクト。 - ネットワークからアクセスされるデータに関連付けられている
LiveData
オブジェクト。
アクティビティで行う必要があるのは、MediatorLiveData
オブジェクトを監視して、両方のソースから最新データを受け取ることだけです。詳しい例については、アプリのアーキテクチャ ガイドの付録: ネットワーク ステータスの公開をご覧ください。
参考情報
LiveData
クラスについて詳しくは、次のリソースをご覧ください。
サンプル
- Sunflower - アーキテクチャ コンポーネントを使用したおすすめの方法を示すデモアプリ
Codelab
- Android Room とビュー(Java)、(Kotlin)
- Kotlin Flow と LiveData を使用した高度なコルーチンの学習
ブログ
- ViewModel と LiveData: パターンとアンチパターン
- ViewModel に勝る LiveData: 変換と MediatorLiveData を使用したリアクティブ パターン
- スナックバー、ナビゲーション、その他のイベントと LiveData(SingleLiveEvent のケース)
動画
あなたへのおすすめ
- 注: JavaScript がオフになっている場合はリンクテキストが表示されます
- ライフサイクル対応コンポーネントで Kotlin コルーチンを使用する
- ライフサイクル対応コンポーネントによるライフサイクルへの対応
- Paging の実装をテストする