LiveData 概述   属于 Android Jetpack 的一部分。

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

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

在使用nameObserver作为参数调用observe()之后,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()ViewModel中将其转换为`LiveData`。在此代码实验室中,了解有关将Kotlin `Flow`与`LiveData`一起使用的更多信息。对于使用Java构建的代码库,请考虑结合使用Executors和回调或RxJava

扩展LiveData

如果观察者的生命周期处于STARTEDRESUMED状态,则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);
    }
}

此示例中价格监听器的实现包括以下重要方法:

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

如果您认为在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组件需要在每次调用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源。每当任何原始LiveData源对象更改时,`MediatorLiveData`对象的观察者就会被触发。

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

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

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

其他资源

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

示例

代码实验室

博客

视频