LiveData 概述   Android Jetpack 的一部分。

LiveData 是一个可观察的数据持有者类。与普通可观察对象不同,LiveData 是生命周期感知的,这意味着它会尊重其他应用组件(如活动、片段或服务)的生命周期。这种感知确保 LiveData 仅更新处于活动生命周期状态的应用组件观察者。

LiveData 将观察者(由 Observer 类表示)视为处于活动状态,如果其生命周期处于 STARTEDRESUMED 状态。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 对象

  1. 创建一个 LiveData 实例来保存某种类型的数据。 这通常在您的 ViewModel 类中完成。
  2. 创建一个 Observer 对象,该对象定义 onChanged() 方法,该方法控制 LiveData 对象保存的数据发生变化时会发生什么。 您通常会在 UI 控制器(例如活动或片段)中创建 Observer 对象。
  3. 使用 observe() 方法将 Observer 对象附加到 LiveData 对象。 observe() 方法接受一个 LifecycleOwner 对象。 这会将 Observer 对象订阅到 LiveData 对象,以便它会收到更改的通知。 您通常会在 UI 控制器(例如活动或片段)中附加 Observer 对象。

当您更新存储在 LiveData 对象中的值时,它会触发所有已注册的观察者,只要附加的 LifecycleOwner 处于活动状态。

LiveData 允许 UI 控制器观察者订阅更新。 当 LiveData 对象持有的数据发生变化时,UI 会自动更新以响应。

创建 LiveData 对象

LiveData 是一个包装器,可以与任何数据一起使用,包括实现 Collections 的对象,例如 ListLiveData 对象通常存储在 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 没有公开可用的方法来更新存储的数据。 MutableLiveData 类公开了 setValue(T)postValue(T) 方法,如果您需要编辑存储在 LiveData 对象中的值,则必须使用这些方法。 通常,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) 的一部分编写。

Room 会生成所有必要的代码,以便在更新数据库时更新 LiveData 对象。 生成的代码在需要时在后台线程上异步运行查询。 此模式对于使 UI 中显示的数据与存储在数据库中的数据保持同步非常有用。 您可以在 Room 持久性库指南 中详细了解 Room 和 DAO。

将协程与 LiveData 配合使用

LiveData 包含对 Kotlin 协程的支持。 有关更多信息,请参阅 将 Kotlin 协程与 Android 架构组件配合使用

应用架构中的 LiveData

LiveData 具有生命周期感知能力,它遵循活动和片段等实体的生命周期。 使用 LiveData 在这些生命周期所有者和其他具有不同生命周期的对象(例如 ViewModel 对象)之间进行通信。 ViewModel 的主要职责是加载和管理与 UI 相关的数据,这使其成为保存 LiveData 对象的绝佳选择。 在 ViewModel 中创建 LiveData 对象,并使用它们将状态公开到 UI 层。

活动和片段不应保存 LiveData 实例,因为它们的作用是显示数据,而不是保存状态。 此外,使活动和片段免于保存数据,可以更容易地编写单元测试。

您可能很想在数据层类中使用 LiveData 对象,但 LiveData 不适合处理异步数据流。 尽管您可以使用 LiveData 变换和 MediatorLiveData 来实现这一点,但此方法有一些缺点:组合数据流的功能非常有限,所有 LiveData 对象(包括通过变换创建的对象)都在主线程上观察。 下面的代码示例展示了如何在 Repository 中保存 LiveData 会阻塞主线程

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() 将它们转换为 LiveData。 在 此代码实验室 中详细了解如何将 Kotlin FlowLiveData 配合使用。 对于使用 Java 构建的代码库,请考虑将 Executors 与回调或 RxJava 配合使用。

扩展 LiveData

LiveData 认为,如果观察者的生命周期处于 STARTEDRESUMED 状态,则观察者处于活动状态。 以下示例代码说明了如何扩展 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);
    }
}

此示例中价格侦听器的实现包含以下重要方法

  • LiveData 对象具有活动观察者时,将调用 onActive() 方法。这意味着您需要从该方法开始观察股票价格更新。
  • LiveData 对象没有任何活动观察者时,将调用 onInactive() 方法。由于没有观察者在监听,因此没有理由保持连接到 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 实例。LiveData 仅在其中一个或多个片段可见且处于活动状态时才连接到系统服务。

转换 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 对象,否则不会计算转换。由于转换是按需计算的,因此生命周期相关的行为会隐式传递下去,而无需额外的显式调用或依赖项。

如果您认为您需要一个 Lifecycle 对象在 ViewModel 对象中,转换可能是一个更好的解决方案。例如,假设您有一个 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 组件需要在每次调用 getPostalCode() 时从先前的 LiveData 对象取消注册并注册到新实例。此外,如果 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 对象的引用,然后在它们之上定义转换规则。

创建新的转换

在您的应用程序中,可能存在许多不同的特定转换,但它们默认情况下不会提供。要实现自己的转换,您可以使用 MediatorLiveData 类,它监听其他 LiveData 对象并处理由它们发出的事件。 MediatorLiveData 正确地将它的状态传播到源 LiveData 对象。要了解有关此模式的更多信息,请参阅 Transformations 类的参考文档。

合并多个 LiveData 源

MediatorLiveDataLiveData 的子类,允许您合并多个 LiveData 源。 MediatorLiveData 对象的观察者会在任何原始 LiveData 源对象发生更改时被触发。

例如,如果您在 UI 中有一个 LiveData 对象,它可以从本地数据库或网络进行更新,那么您可以将以下源添加到 MediatorLiveData 对象中

  • 一个与存储在数据库中的数据关联的 LiveData 对象。
  • 一个与从网络访问的数据关联的 LiveData 对象。

您的活动只需要观察 MediatorLiveData 对象以接收来自两个来源的更新。有关详细示例,请参阅 附录:公开网络状态 部分中的 应用程序架构指南

其他资源

要了解有关 LiveData 类的更多信息,请参考以下资源。

示例

Codelabs

博客

视频