Android LiveData详解

官方文档翻译

  • 1.LiveData概述
    • 1.1 使用LiveData的优点
    • 1.2 使用LiveData对象
      • 1.2.1 创建LiveData对象
      • 1.2.2 观察LiveData对象
      • 1.2.3 更新LiveData对象
      • 1.2.4 在Room中使用LiveData
    • 1.3 继承LiveData
    • 1.4 LiveData变换
      • 1.4.1 创建新的转换
    • 1.5 合并多个LiveData数据源
    • 1.6 额外资源

1.LiveData概述

LiveData是一个observable数据持有类。与常规observable不同,LiveData是生命周期感知的,这意味着它跟随其他应用程序组件(如activities, fragments, or services)的生命周期。这种感知能力确保LiveData只更新处于活跃生命周期状态的应用程序组件。

注意:在Android工程中导入LiveData组件,请看Adding Components to your Project

LiveData与一个Observer关联,如果观察者的生命周期处于STARTED或RESUMED状态,则表示观察者处于活动状态。LiveData只通知活跃的观察者做更新。注册到LiveData对象中的不活跃的观察者则得不到数据更新的通知。

您可以注册一个observer并与实现了LifecycleOwner接口的对象配对。这种关系允许当相应的Lifecycle对象的状态改变为DESTROYED时,观察者被移除。这对于activities and fragments尤其有用,因为它们可以安全地观察LiveData对象,而不用担心泄漏——当activities 和 fragments的生命周期被销毁时,它们会立即取消订阅。

有关如何使用LiveData的更多信息,请参见使用LiveData对象。

1.1 使用LiveData的优点

使用LiveData具有以下优点:

  1. 确保UI与数据状态匹配

LiveData遵循观察者模式。当生命周期状态改变时,LiveData通知Observer对象。您可以合并代码来更新这些观察者对象中的UI。观察者可以在数据每次有更新时更新UI。

  1. 没有内存泄漏

观察者绑定到生命周期对象,并在其关联的生命周期是destroyed时自行清理。

  1. 停止activities造成的crash问题

如果观察者的生命周期是不活动的,比如在堆栈下面的activity,那么它接收不到任何LiveData事件。

  1. 不再手动管理生命周期

UI组件仅仅观察相关数据,不停止或恢复观察。LiveData自动管理所有这一切,因为在观察的时候它能感知到相关的生命周期状态变化。

  1. 始终保持最新数据

如果生命周期变得不活动,则在再次激活时接收最新数据。例如,后台中的activity在返回到前台后立即接收最新数据。

  1. 及时响应配置改变

如果由于配置改变(如设备旋转)而重新创activity或fragment,则它立即接收最新的可用数据。

  1. 资源共享

您可以使用Sigelon模式集成LiveData对象来包装系统服务,以便它们可以在您的应用程序中共享。一旦LiveData对象连接到系统服务,然后需要用到该资源的任何observer都可以观察到LiveData对象。有关更多信息,请参见扩展LiveData。

1.2 使用LiveData对象

按照以下步骤使用LiveData对象:

创建一个LiveData实例来保存某种类型的数据。这通常是在ViewModel类中完成的。

创建一个Observer对象,该对象定义onChanged()方法,该方法响应LiveData对象中数据更改时发生的变化。通常在UI控制器中创建一个Observer对象,例如activity或fragment。

使用observe()方法将观察者对象与LiveData对象关联到一块。observe()方法使用LifecycleOwner对象。这将Observer对象向LiveData对象订阅,以便通知其更改。通常在UI控制器中添加Observer对象,例如activity 或者 fragment。

注意:您可以使用observeForever(Observer)方法注册一个没有关联LifecycleOwner对象的观察者。在这种情况下,观察者被认为总是活跃的,因此总是被通知更新。您可以通过调用removeObserver(Observer)方法删除这些观察者。

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

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

1.2.1 创建LiveData对象

LiveData是一种可以与任何数据一起使用的包装器,包括实现Collections的对象,如List。LiveData对象通常存储在ViewModel对象中,并通过getter方法访问,如下面的示例所示:

public class NameViewModel extends ViewModel {

// Create a LiveData with a String
private MutableLiveData mCurrentName;

    public MutableLiveData getCurrentName() {
        if (mCurrentName == null) {
            mCurrentName = new MutableLiveData();
        }
        return mCurrentName;
    }

// Rest of the ViewModel...
}

最初,LiveData对象中的数据没有设置。

注意:请确保将更新UI的LiveData对象存储在ViewModel对象中,而不是activity 或者 fragment中,原因如下:

  • 避免臃肿的activities和fragments。现在这些UI控制器负责显示数据,但不保存数据状态。

  • 将LiveData实例与特定activity 或者 fragment实例解耦, 并允许LiveData对象在配置更改时存活。

您可以ViewModel向导中阅读更多关于ViewModel类的优点和用法。

1.2.2 观察LiveData对象

在大多数情况下,应用程序组件的onCreate()方法是开始观察LiveData对象的正确位置,原因如下:

以确保系统不从activity或fragment的onResume()方法中进行多余调用。

以确保activity或fragment具有可在其活跃后立即显示的数据。一旦应用程序组件处于STARTED状态,它就会从正在观察的LiveData对象接收最新的值。只有在LiveData对象被设置为可观察状态时才会发生。

通常,LiveData只在数据更改时才提供更新,并且只对活动的observers提供更新。这种行为的一个例外是,观察者在从非活动状态转变为活动状态时也会收到更新。此外,如果观察者第二次从非活动状态改变为活动状态,它仅接收到一个更新,如果自上一次变为活动状态的值发生了改变。

下面的示例代码说明如何开始观察LiveData对象:

public class NameActivity extends AppCompatActivity {

    private NameViewModel mModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Other code to setup the activity...

        // Get the ViewModel.
        mModel = ViewModelProviders.of(this).get(NameViewModel.class);


        // Create the observer which updates the UI.
        final Observer nameObserver = new Observer() {
            @Override
            public void onChanged(@Nullable final String newName) {
                // Update the UI, in this case, a TextView.
                mNameTextView.setText(newName);
            }
        };

        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        mModel.getCurrentName().observe(this, nameObserver);
    }
}

使用作为参数传递的nameObserver调用observe()之后,立即调用onChanged()以提供存储在mCurrentName中的最新值。如果LiveData对象没有在mCurrentName中设置值,则不调用onChanged()。

1.2.3 更新LiveData对象

LiveData没有公开可用的方法来更新存储的数据。MutableLiveData类公开了setValue(T)和postValue(T)方法,如果需要编辑LiveData对象中存储的值,则必须使用这些方法。通常在ViewModel中使用MutableLiveData,然后ViewModel只向observers公开不可变的LiveData对象。

在建立了观察者关系之后,然后可以更新LiveData对象的值,如下面的示例所示,当用户点击按钮时触发所有观察者:

mButton.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        String anotherName = "John Doe";
        mModel.getCurrentName().setValue(anotherName);
    }
});

调用实例中的setValue(T)并传递John Doe作为参数会导致观察者调用它们的onChanged()方法。该示例显示了按下按钮,但是由于各种原因,可以调用setValue()postValue()来更新mName,包括响应网络请求或数据库加载完成;在所有情况下,对setValue()或postValue()的调用都会触发观察者并更新UI。

注意:您必须调用setValue(T) 方法来从主线程更新LiveData对象。如果在工作线程中执行代码,则可以使用postValue(T)方法来更新LiveData对象。

1.2.4 在Room中使用LiveData

Room持久性库支持可观察的查询,这些查询返回LiveData对象。Observable查询是作为数据库访问对象(DAO)的一部分编写的。

当数据库被更新时,Room生成所有必要的代码来更新LiveData对象。生成的代码在需要时异步地在后台线程上运行查询。此模式对于保持UI中显示的数据与存储在数据库中的数据同步是有用的。您可以阅读更多关于Room和DAOs在Room持久库指南。

1.3 继承LiveData

如果观察者的生命周期处于STARTED或RESUMED状态,则LiveData认为观察者处于活动状态。

public class StockLiveData extends LiveData {
    private StockManager mStockManager;

    private SimplePriceListener mListener = new SimplePriceListener() {
        @Override
        public void onPriceChanged(BigDecimal price) {
            setValue(price);
        }
    };

    public StockLiveData(String symbol) {
        mStockManager = new StockManager(symbol);
    }

    @Override
    protected void onActive() {
        mStockManager.requestPriceUpdates(mListener);
    }

    @Override
    protected void onInactive() {
        mStockManager.removeUpdates(mListener);
    }
}

本例中价格listener的实现包括以下重要方法:

当LiveData对象具有活动的观察者时, 调用onActive()方法。这意味着你需要从这个方法开始观察股票价格的更新。

LiveData对象没有任何活动的观察者时调用onInactive()方法。由于没有观察者在监听,所以没有理由保持与StockManager服务的连接。

setValue(T) 方法更新LiveData实例的值,并将变化通知给任何活动的观察者。

您可以使用StockLiveData类如下:

public class MyFragment extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        LiveData myPriceListener = ...;
        myPriceListener.observe(this, price -> {
            // Update the UI.
        });
    }
}

observe()方法将作为LifecycleOwner实例的fragment作为第一个参数传递。这样做意味着这个观察者被绑定到与所有者相关联的Lifecycle对象,意思是:

  • 如果生命周期对象不处于活动状态,则即使值改变,也不会调用observer。

  • 在生命周期对象被销毁后,观察者将被自动移除。

LiveData对象是生命周期感知的,这意味着您可以在多个activities, fragments, 和 services之间共享它们。为了保持示例简单,可以将LiveData类作为单例的实现如下:

public class StockLiveData extends LiveData {
    private static StockLiveData sInstance;
    private StockManager mStockManager;

    private SimplePriceListener mListener = 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) {
        mStockManager = new StockManager(symbol);
    }

    @Override
    protected void onActive() {
        mStockManager.requestPriceUpdates(mListener);
    }

    @Override
    protected void onInactive() {
        mStockManager.removeUpdates(mListener);
    }
}

你可以在片段中使用它如下:

public class MyFragment extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        StockLiveData.get(getActivity()).observe(this, price -> {
            // Update the UI.
        });
    }
}

多个fragments和activities可以观察MyPriceListener实例。LiveData只连接系统服务,如果其中一个或多个系统可见和激活。

1.4 LiveData变换

在将LiveData对象分发给观察者之前,您可能需要对存储在LiveData对象中的值进行更改,或者您可能需要基于另一个LiveData对象的值返回不同的LiveData实例。Lifecycle包提供转换类,其中包含支持这些方案的帮助方法。

Transformations.map()

对存储在LiveData对象中的值应用函数,并将结果传播到下游。

LiveData userLiveData = ...;
LiveData userName = Transformations.map(userLiveData, user -> {
    user.name + " " + user.lastName
});

Transformations.switchMap()

类似于map(),将函数应用到LiveData对象中存储的值中,并解包并将结果分发到下游。传递到switchMap()的函数必须返回一个LiveData对象,如下面的示例所示:

private LiveData getUser(String id) {
  ...;
}

LiveData userId = ...;
LiveData user = Transformations.switchMap(userId, id -> getUser(id) );

可以使用transformation方法在观察者的生命周期中传递信息。除非观察者正在观看返回的LiveData对象,否则这些转换不会被计算。因为转换是惰性计算的,所以与生命周期相关的行为是隐式传递的,而不需要额外的显式调用或依赖。

如果您认为在ViewModel对象中需要一个Lifecycle对象,转换可能是更好的解决方案。例如,假设您有一个UI组件,它接受一个地址并返回该地址的邮政编码。可以通过以下示例代码说明该组件的初级的ViewModel模型:

class MyViewModel extends ViewModel {
    private final PostalCodeRepository repository;
    public MyViewModel(PostalCodeRepository repository) {
       this.repository = repository;
    }

    private LiveData getPostalCode(String address) {
       // DON'T DO THIS
       return repository.getPostCode(address);
    }
}

然后,UI组件需要从以前的LiveData对象中注销注册,并在每次调用getPostalCode()时注册到新实例。此外,如果UI组件被重新创建,它将触发对repository.getPostCode()方法的另一个调用,而不是使用前一个调用的结果。

相反,您可以实现postalcode 查询作为地址输入的转换,如下面的示例所示:

class MyViewModel extends ViewModel {
    private final PostalCodeRepository repository;
    private final MutableLiveData addressInput = new MutableLiveData();
    public final LiveData 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字段是publicfinal,因为字段从不更改。postalCode字段定义为addressInput的转换,这意味着在addressInput更改时调用repository.getPostCode()方法。如果存在活动观察者,则这是正确的,如果在调用repository.getPostCode()时没有活动观察者,则在添加观察者之前不进行任何计算。

该机制允许应用程序的较低级别创建按需求计算的LiveData对象。ViewModel对象可以很容易地获得对LiveData对象的引用,然后在它们上面定义转换规则。

1.4.1 创建新的转换

在你的应用程序中有十几种不同的特定转换,但默认情况下它们不被提供。为了实现您自己的转换,您可以使用MediatorLiveData类,该类侦听其他LiveData对象并处理它们发出的事件。MediatorLiveData正确地将其状态传播到Source LiveData对象。若要了解此模式的更多信息,请参见Transformations的参考文档。

1.5 合并多个LiveData数据源

MediatorLiveData是LiveData的子类,允许您合并多个LiveData源。然后,每当原始LiveData源对象改变时,就会触发MediatorLiveData对象的观察者。

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

  • 与数据库中存储的数据相关联的LiveData对象。

  • 与从网络访问的数据相关联的LiveData对象。

您的activity只需要观察MediatorLiveData对象以从两个源接收更新。有关详细示例,请参阅附录:App体系结构指南中公开网络状态部分。

1.6 额外资源

LiveData是一个Android Jetpack架构组件。在Sunflower demo应用程序中使用它。

有关使用LiveData与Snackbar消息、navigation事件和其他事件有关的信息,请阅读此帖子。

你可能感兴趣的:(Android开源框架)