2、Android Jetpack_Note_CodeLabs一Lifecycles+ViewModel+LiveData

1、简介

本篇文章向您介绍了用于构建Android应用程序的如下生命周期感知架构组件:html

  • ViewModel - 提供了一种建立和检索绑定到特定生命周期的对象的方法。A ViewModel一般存储视图数据的状态,并与其余组件通讯,例如数据存储库或处理业务逻辑的域层。要阅读本主题的介绍性指南,请参阅ViewModel
  • LifecycleOwner / LifecycleRegistryOwner -不管是LifecycleOwner和LifecycleRegistryOwner是在实现的接口AppCompatActivity和Support Fragment类。您能够将其余组件订阅到实现这些接口的全部者对象,以观察对全部者生命周期的更改。要阅读本主题的介绍性指南,请参阅Hanlde Lifecycles
  • LiveData - 容许您观察应用程序的多个组件之间的数据更改,而无需在它们之间建立明确,严格的依赖关系路径。LiveData尊重应用程序组件的复杂生命周期,包括活动,片断,服务或LifecycleOwner应用程序中定义的任何内容。LiveData经过暂停对已中止LifecycleOwner对象的订阅以及取消对LifecycleOwner已完成对象的订阅来管理观察者订阅。要阅读本主题的介绍性指南,请参阅LiveData

1.1 你要建造什么

在此代码框中,您能够实现上述每一个组件的示例。首先是示例应用程序,而后经过一系列步骤添加代码,在进度时集成各类体系结构组件。java

1.2 你须要什么

  • Android Studio 3.3或更高版本
  • 熟悉Android的生命周期

2、环境搭建

下载代码android

git clone git@github.com:googlecodelabs / android-lifecycles.git
复制代码

运行程序:git

旋转屏幕,注意计时器重置github

若是咱们按照以前的逻辑须要保存状态,而后恢复状态,并恢复计时器。如今您能够用ViewModel,由于由于此类的实例在配置更改后仍然存在,例如屏幕旋转。shell

3、添加ViewModel

在此步骤中,您使用ViewModel来跨屏幕旋转保持状态并解决您在上一步中观察到的行为。在上一步中,您运行了一个显示计时器的活动。当配置更改(例如屏幕旋转)破坏活动时,将重置此计时器。缓存

可使用ViewModel在活动或片断的整个生命周期中保留数据。如前一步所示,活动是管理应用数据的不良选择。活动和片断是短暂的对象,当用户与应用程序交互时,这些对象会频繁建立和销毁。 ViewModel还更适合管理与网络通讯相关的任务,以及数据操做和持久性。bash

3.1 使用ViewModel保持计时器的状态

打开ChronoActivity2并检查类如何检索和使用ViewModel网络

ChronometerViewModel chronometerViewModel
        = ViewModelProviders.of(this).get(ChronometerViewModel.class);
复制代码

this指的是一个实例LifecycleOwner。ViewModel只要活动范围存在,框架就会保持LifecycleOwner活力。ViewModel若是其全部者因配置更改(例如屏幕旋转)而被销毁,则不会销毁A. 全部者的新实例从新链接到现有实例,ViewModel以下图所示:架构

Activity或Fragment的范围从建立到完成(或终止),您不得与销毁混淆。请记住,当旋转设备时,活动会被销毁,但ViewModel与之关联的任何实例都不会被销毁。

3.1 试一下

运行应用程序(在“运行配置”下拉列表中选择“ 步骤2 ”),并在执行如下任一操做时确认计时器未重置:

  1. 旋转屏幕。 2。 导航到另外一个应用程序,而后返回。

可是,若是您或系统退出应用程序,则计时器将重置。

系统会ViewModel在生命周期全部者的整个生命周期中保留内存中的实例,例如片断或活动。系统不会持久ViewModel存储长期存储。

4、使用LiveData包装数据

在此步骤中,您将使用a的自定义计时器替换前面步骤中使用的计时器Timer,并每秒更新UI。 Timer是一个java.util可用于在未来循环安排任务的类。您将此逻辑添加到LiveDataTimerViewModel类中,并使活动专一于管理用户和UI之间的交互。

当计时器通知时,活动会更新UI。为了不内存泄漏,ViewModel不包括对活动的引用。例如,配置更改(例如屏幕旋转)可能会致使对ViewModel应该进行垃圾回收的活动的引用。系统将保留实例,ViewModel直到相应的活动或生命周期全部者再也不存在。

注意:存储到一个参考上下文或视图中ViewModel可能会致使内存泄漏。避免使用引用Context或View类实例的字段。所述onCleared()方法是用于清除引用有用退订或明确的引用与长周期其它的目的,但不Context或View对象。

ViewModel您能够将活动或片断配置为观察数据源,而不是直接从其中修改视图,并在数据更改时接收数据。这种安排称为观察者模式。

注意:要将数据公开为可观察对象,请将类型包装在LiveData类中。

若是您使用了数据绑定库或其余反应库(如RxJava),您可能熟悉观察者模式。LiveData是一个特殊的可观察类,它是生命周期感知的,只通知活跃的观察者。

4.1 LifecycleOwner

ChronoActivity3是一个实例LifecycleActivity,它能够提供生命周期的状态。这是类声明:

public class LifecycleActivity extends FragmentActivity implements LifecycleRegistryOwner {...}
复制代码

将LifecycleRegistryOwner用于的实例的生命周期结合ViewModel并LiveData与活动或片断。片断的等价类是LifecycleFragment。

4.2 更新ChronoActivity

1.将如下代码添加到方法中的ChronoActivity3类中subscribe()以建立订阅:

mLiveDataTimerViewModel.getElapsedTime().observe(this, elapsedTimeObserver);
复制代码

2.接下来,在LiveDataTimerViewModel类中设置新的通过时间值。找到如下注释

//TODO set the new value
复制代码

用如下语句替换注释:

mElapsedTime.postValue(newValue);
复制代码

3.运行应用程序并在Android Studio中打开Android Monitor。请注意,除非您导航到另外一个应用程序,不然日志会每秒更新。若是您的设备支持多窗口模式,您可能想尝试使用它。旋转屏幕不会影响应用的行为方式。

注意:LiveData对象仅在活动时发送更新,或者处于LifecycleOwner活动状态。若是您导航到其余应用程序,日志消息会暂停,直到您返回。LiveData对象仅在其各自的生命周期全部者为STARTED或时将订阅视为活动的RESUMED。

5、订阅生命周期事件

许多Android组件和库要求您:

  1. 订阅或初始化组件或库。
  2. 取消订阅或中止组件或库。

未能完成上述步骤可能会致使内存泄漏和细微错误。

能够将生命周期全部者对象传递给生命周期感知组件的新实例,以确保它们了解生命周期的当前状态。

您可使用如下语句查询生命周期的当前状态:

lifecycleOwner.getLifecycle().getCurrentState()
复制代码

上面的语句返回一个状态,例如Lifecycle.State.RESUMED,或Lifecycle.State.DESTROYED。

实现的生命周期感知对象LifecycleObserver还能够观察生命周期全部者状态的变化:

lifecycleOwner.getLifecycle().addObserver(this);
复制代码

您能够注释对象以指示它在须要时调用适当的方法:

@OnLifecycleEvent(Lifecycle.EVENT.ON_RESUME)
void addLocationListener() { ... }
复制代码

5.1 建立一个支持生命周期的组件

在此步骤中,您将建立一个对活动生命周期全部者做出反应的组件。使用片断做为生命周期全部者时,相似的原则和步骤适用。

您使用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当它改变时被更新:

6、在Fragment之间共享ViewModel

6.1 在Fragment之间共享ViewModel

使用ViewModel完成如下附加步骤在Fragment之间的通讯:

  • 一项活动。
  • 一个片断的两个实例,每一个都有一个SeekBar。
  • 单个ViewModel带LiveData字段。

运行此步骤并注意其中两个实例SeekBar彼此独立:

使用ViewModel以便在SeekBar更改Fragment时SeekBar更新另外一个Fragment:

注意:您应该将活动用做生命周期全部者,由于每一个Fragment的生命周期是独立的。

7、跨进程保持ViewModel状态(实验版)

本节中使用的模块处于alpha阶段。这意味着API不是最终的,未来可能会发生变化。

从内存管理概述:

当用户在应用程序之间切换时,Android会保留非前景的应用程序 - 即用户不可见或在最近最少使用(LRU)缓存中运行音乐播放等前台服务。例如,当用户首次启动应用程序时,会为其建立一个进程; 可是当用户离开应用程序时,该进程不会退出。系统会保持进程缓存。若是用户稍后返回应用程序,系统将从新使用该过程,从而使应用程序切换更快。

因为系统内存不足,它会从最近最少使用的进程开始杀死缓存中的进程。当用户导航回应用程序时,系统将在新进程中从新启动应用程序。

因为只有在用户暂时没有与应用程序交互时才会发生这种状况,所以可能容许他们返回应用程序并在初始状态下找到它。可是,在某些状况下,您可能但愿保存应用程序的状态或部分应用程序的状态,以便在进程被杀死时不会丢失该信息。

该 lifecycle-viewmodel-savedstate模块提供对ViewModel中已保存状态的访问。

该模块的gradle依赖是

"androidx.lifecycle:lifecycle-viewmodel-savedstate:$savedStateVersion"
复制代码

ViewModels须要两次更改才能访问已保存的状态:

  1. 传递SavedStateVMFactory到ViewModelProvider
  2. 添加一个接收a的构造函数 SavedStateHandle

首先,让咱们在没有这些变化的状况下尝试第6步:

1.打开运行配置“步骤6”

你会看到一个简单的表单:

2.更更名称,而后单击“保存”。这将把它存储在ViewModel内的LiveData中。

3.模拟系统终止进程(须要运行P +的仿真器)。首先输入如下命令确保进程正在运行:

$ adb shell ps -A | grep lifecycle
复制代码

这应该输出带有名称的运行进程 com.example.android.codelabs.lifecycle

在设备或模拟器上按Home,而后运行

$ adb shell am kill com.example.android.codelabs.lifecycle
复制代码

若是你再输入一次

$ adb shell ps -A | grep lifecycle
复制代码

你应该什么也得不到,代表这个过程已被正确杀死。

4.再次打开应用程序(在应用程序启动器中查找LC Step6)。

ViewModel中的值未保留,但已EditText恢复其状态。这怎么可能?

一些UI元素,包括EditText使用本身的onSaveInstanceState实现保存其状态。在进程被终止后,此状态将在配置更改后恢复时恢复。阅读ViewModels:Persistence,onSaveInstanceState(),恢复UI状态和加载器以获取更多信息。

实际上,该lifecycle-viewmodel-savedstate模块还使用onSaveInstanceState和onRestoreInstanceState保持ViewModel状态,但它使这些操做更方便。

为ViewModel实现保存的状态 在SavedStateActivity.java文件中,替换

mSavedStateViewModel = ViewModelProviders.of(this).get(SavedStateViewModel.class);
复制代码

为:

mSavedStateViewModel = ViewModelProviders.of(this, new SavedStateVMFactory(this))
        .get(SavedStateViewModel.class);
复制代码

在SavedStateViewModel.java文件中,您须要添加一个新的构造函数,该构造函数SavedStateHandle将状态存储在私有字段中:

private SavedStateHandle mState;

public SavedStateViewModel(SavedStateHandle savedStateHandle){
   mState = savedStateHandle;
}
复制代码

如今您将使用该模块的LiveData支持,所以您再也不须要存储它:

private static final String NAME_KEY =“name”;

//公开不可变的LiveData
LiveData <String> getName(){
    return mState.getLiveData(NAME_KEY);
}

void saveNewName(String newName){
    mState.set(NAME_KEY,newName);
}
复制代码

如今您正在使用LiveData mState,MutableLiveData名称再也不使用,能够删除。

如今您能够再次尝试相同的过程。打开应用程序,更更名称并保存。而后,按Home并使用如下命令终止该过程:

$ adb shell am kill com.example.android.codelabs.lifecycle
复制代码

若是您从新打开该应用程序,您将看到ViewModel中的状态此时已保存。

相关文章
相关标签/搜索