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 オブジェクトを操作する手順は次のとおりです。

  1. 特定のタイプのデータを保持する LiveData のインスタンスを作成します。これは通常、ViewModel クラス内で行います。
  2. onChanged() メソッドを定義する Observer オブジェクトを作成します。このオブジェクトは、LiveData オブジェクトが保持するデータが変更されたときの処理を管理します。通常は、UI コントローラ(アクティビティやフラグメントなど)内に Observer オブジェクトを作成します。
  3. 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) メソッドを使用する必要があります。通常、MutableLiveDataViewModel で使用され、その場合、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 オブジェクトの保持に適しています。ViewModelLiveData オブジェクトを作成し、これを使用して状態を UI レイヤに公開します。

アクティビティとフラグメントの役割は、状態の保持ではなくデータの表示であるため、LiveData インスタンスを保持する必要はありません。また、アクティビティとフラグメントがデータを保持しないようにすることで、単体テストの作成が容易になります。

データレイヤ クラスの LiveData オブジェクトを利用しようとしても、LiveData は非同期データ ストリームを処理するようには設計されていません。これは LiveData 変換と MediatorLiveData を使用して実現できますが、このアプローチには欠点があります。データ ストリームを組み合わせる機能が非常に限られており、LiveData オブジェクト(変換によって作成されたものを含む)はすべて、メインスレッドで監視されます。次のコードは、LiveDataRepository に保持することでメインスレッドをブロックする方法の例を示しています。

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() を使用して ViewModelLiveData に変換することをご検討ください。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 ソースを統合する

MediatorLiveDataLiveData のサブクラスです。このサブクラスを使用すると、複数の LiveData ソースを統合できます。元の LiveData ソース オブジェクトのいずれかが変更されると、MediatorLiveData オブジェクトのオブザーバーが必ずトリガーされます。

たとえば、ローカル データベースやネットワークから更新可能な LiveData オブジェクトが UI 内にある場合、以下のソースを MediatorLiveData オブジェクトに追加できます。

  • データベースに格納されているデータに関連付けられている LiveData オブジェクト。
  • ネットワークからアクセスされるデータに関連付けられている LiveData オブジェクト。

アクティビティで行う必要があるのは、MediatorLiveData オブジェクトを監視して、両方のソースから最新データを受け取ることだけです。詳しい例については、アプリのアーキテクチャ ガイド付録: ネットワーク ステータスの公開をご覧ください。

参考情報

LiveData クラスについて詳しくは、次のリソースをご覧ください。

サンプル

Codelabs

ブログ

動画