【译文|源码】安卓架构组件ViewModel+Lifecycle
介绍
组件
架构组件是一组Android库,可帮助您以强健,可测试和可维护的方式构建应用程序。
这个codelab向您介绍了构建Android应用程序的以下生命周期感知架构组件:
ViewModel提供了一种方法来创建和检索绑定到特定生命周期的对象。A ViewModel通常存储视图数据的状态,并与其他组件(如数据存储库或处理业务逻辑的域层)进行通信。要阅读本主题的入门指南,请参阅ViewModel。
LifecycleOwner / LifecycleRegistryOwner无论是LifecycleOwner和LifecycleRegistryOwner是在实现的接口AppCompatActivity和Support Fragment类。您可以将其他组件订阅到实现这些接口的所有者对象,以观察对所有者生命周期的更改。要阅读关于此主题的入门指南,请参阅处理生命周期。
LiveData允许您在应用程序的多个组件中观察对数据的更改,而无需在它们之间创建明确的,严格的依赖路径。LiveData尊重您的应用程序组件的复杂生命周期,包括活动,片段,服务或LifecycleOwner您的应用程序中定义的任何内容。LiveData通过暂停对已停止LifecycleOwner对象的订阅来管理观察者订阅,并取消LifecycleOwner已完成对象的订阅。要阅读本主题的入门指南,请参阅LiveData。
你会建立什么
在这个代码中,你实现了上面描述的每个组件的例子。您从一个示例应用程序开始,通过一系列步骤添加代码,随着您的进步整合各种体系结构组件。
你需要什么
Android Studio 2.3或更高版本。
熟悉Android 活动生命周期。
第1步 - 设置您的环境
在这一步中,您下载整个codelab的代码,然后运行一个简单的示例应用程序。
点击下面的按钮来下载这个codelab的所有代码:
1、解压缩代码,然后打开项目Android Studio版本2.3或更高版本。
2、在设备或模拟器上运行Step 1运行配置。
该应用程序运行并显示类似于以下屏幕截图的屏幕:

3、旋转屏幕并注意定时器重置!
您现在需要更新应用程序以跨屏幕旋转保持状态。您可以使用一个,ViewModel因为这个类的实例存在配置更改,如屏幕旋转。
第2步 - 添加一个ViewModel
在这个步骤中,你使用一个ViewModel持久的状态跨屏幕旋转,并解决你在前面的步骤中观察到的行为。在之前的步骤中,您运行了一个显示计时器的活动。当配置更改(如屏幕旋转)破坏活动时,此计时器将重置。
您可以使用a ViewModel在活动或片段的整个生命周期中保留数据。正如前面的步骤所示,一个活动是管理应用程序数据的糟糕选择。活动和片段是短暂的对象,在用户与应用程序交互时经常创建和销毁。A ViewModel也更适合于管理与网络通信相关的任务,以及数据操作和持久性。
使用ViewModel保持天文钟的状态
打开ChronoActivity2并检查该类如何检索并使用a ViewModel:
ChronometerViewModel chronometerViewModel
= ViewModelProviders.of(this).get(ChronometerViewModel.class);
this指的是一个实例LifecycleOwner。ViewModel只要活跃的范围,框架就会保持LifecycleOwner活力。ViewModel如果其所有者因配置更改(如屏幕旋转)而被销毁,则A 不会被销毁。所有者的新实例重新连接到现有的ViewModel,如下图所示:

警告:活动或片段的范围从创建到完成(或终止),您不得混淆与破坏。请记住,当设备被旋转时,活动被破坏,但ViewModel与其关联的任何实例都不被破坏。
试试看
运行应用程序(在“运行配置”下拉菜单中选择“ 步骤2 ”),并确认在执行以下任一操作时计时器不会重置:
旋转屏幕。
导航到另一个应用程序,然后返回。

但是,如果您或系统退出应用程序,则定时器将重置。
警告:系统ViewModel在生命周期所有者的整个生命周期中(例如片段或活动)持续存在内存中的实例。系统不会将实例ViewModel存储到长期存储中。
第3步 - 使用LiveData封装数据
在这个步骤中,您将使用前面步骤中使用的天文台替换为自定义天文台Timer,并且每秒更新一次UI。A Timer是java.util您可以用来重复计划未来任务的类。您将此逻辑添加到LiveDataTimerViewModel该类,并将该活动留在管理用户和UI之间的交互。
当定时器通知它时,活动更新UI。为了帮助避免内存泄漏,ViewModel不包括对活动的引用。例如,配置更改(如屏幕旋转)可能会导致ViewModel应该被垃圾收集的活动的引用。系统保留实例,ViewModel直到相应的活动或生命周期所有者不再存在。
注意:存储到一个参考上下文或视图中ViewModel可能会导致内存泄漏。避免引用Context或View类的实例的字段。所述onCleared()方法是用于清除引用有用退订或明确的引用与长周期其它的目的,但不Context或View对象。
不是直接从视图中修改视图ViewModel,而是配置活动或片段来观察数据源,并在数据发生更改时接收数据。这种安排被称为观察者模式。
注意:要将数据作为observable公开,请将该类型包装到LiveData类中。
如果您已经使用了数据绑定库或RxJava等其他反应性库,则可能熟悉观察者模式。LiveData是一个特殊的可观察类,它是生命周期感知的,只能通知活跃的观察者。
LifecycleOwner
ChronoActivity3是一个LifecycleActivity可以提供生命周期状态的实例。这是类声明:
public class LifecycleActivity extends FragmentActivity implements LifecycleRegistryOwner {...}
将LifecycleRegistryOwner用于的实例的生命周期结合ViewModel并LiveData与活动或片段。片段的等价类是LifecycleFragment。
更新ChronoActivity
1.将下面的代码添加到方法中的ChronoActivity3类中subscribe(),以创建订阅:
mLiveDataTimerViewModel.getElapsedTime().observe(this, elapsedTimeObserver);
2.在LiveDataTimerViewModel类中设置新的经过时间值。找到以下评论:
//TODO set the new value
用下面的语句替换注释:
mElapsedTime.setValue(newValue);
3.运行应用程序并在Android Studio中打开Android监视器。注意日志每秒更新,除非您导航到另一个应用程序。如果您的设备支持多窗口模式,您可以尝试使用它。旋转屏幕不会影响应用程序的行为。

注意:LiveData对象只在活动时发送更新,或者处于LifecycleOwner活动状态。如果您导航到不同的应用程序,则日志消息会暂停,直到您返回。LiveData对象只考虑订阅作为活性时它们各自的生命周期所有者是任一STARTED或RESUMED。
第4步 - 订阅生命周期事件
许多Android组件和库要求您:
- 订阅或初始化组件或库。
- 取消订阅,或停止组件或库。
未能完成上述步骤可能导致内存泄漏和微妙的错误。
生命周期所有者对象可以传递给生命周期感知组件的新实例,以确保他们知道生命周期的当前状态。
您可以使用以下语句查询生命周期的当前状态:
lifecycleOwner.getLifecycle().getCurrentState()
上面的语句返回一个状态,比如Lifecycle.State.RESUMED,或者Lifecycle.State.DESTROYED。
实现的生命周期感知对象LifecycleObserver还可以观察生命周期所有者状态的变化:
lifecycleOwner.getLifecycle().addObserver(this);
您可以注释该对象以指示它在需要时调用适当的方法:
@OnLifecycleEvent(Lifecycle.EVENT.ON_RESUME)
void addLocationListener() { ... }
创建一个生命周期感知组件
在此步骤中,您将创建一个对活动生命周期所有者作出反应的组件。使用片段作为生命周期所有者时,应用类似的原则和步骤。
您可以使用Android框架LocationManager获取当前的经纬度并将其显示给用户。此添加允许您:
- 订阅更改并使用自动更新UI LiveData。
- LocationManager基于对活动状态的更改,创建该注册和取消注册的包装。
您通常会订阅一个活动或方法的LocationManager更改,并删除或方法中的侦听器:
onStart()onResume()onStop()onPause()
// Typical use, within an activity.
@Override
protected void onResume() {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mListener);
}
@Override
protected void onPause() {
mLocationManager.removeUpdates(mListener);
}
在这个步骤中,你使用一个LifecycleOwner叫做LifecycleRegistryOwner的类的实现BoundLocationManager。BoundLocationManager类的名字是指类的实例绑定到活动的生命周期的事实。
为了让课堂观察活动的生命周期,您必须将其作为观察者添加。为了达到这个BoundLocationManager目的,通过将下面的代码添加到其构造函数中来指示对象观察生命周期:
lifecycleOwner.getLifecycle().addObserver(this);
要在生命周期更改发生时调用方法,可以使用@OnLifecycleEvent注释。使用类中的以下注释更新addLocationListener()和removeLocationListener()方法BoundLocationListener:
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
void addLocationListener() {
...
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
void removeLocationListener() {
...
}
注意:观察者被带到提供者的当前状态,所以不需要addLocationListener()从构造函数调用。当观察者被添加到生命周期所有者时,它会被调用。
运行应用程序并确认日志监视器在您旋转设备时显示以下操作:
D / BoundLocationMgr:添加了监听器
D / BoundLocationMgr:删除了监听器
D / BoundLocationMgr:添加了监听器
D / BoundLocationMgr:删除了监听器
使用Android模拟器模拟更改设备的位置(单击三个点以显示扩展控件)。在TextView当它改变时被更新:

第5步 - 在片段之间共享ViewModel
使用a完成以下附加步骤ViewModel以启用片段之间的通信并执行以下操作:
- 一个单一的活动。
- 一个片段的两个实例,每个都有一个SeekBar。
- 一个单一ViewModel的LiveData领域。
运行这个步骤,注意两个实例是SeekBar相互独立的:

连接碎片,ViewModel以便SeekBar更改一个时,另一个SeekBar更新:

注意:您应该使用该活动作为生命周期所有者,因为每个片段的生命周期是独立的。