Android(安卓)Jetpack之生命周期的处理
目录
- Lifecycle
- LifecycleOwner
- 实现自定义LifecycleOwner
- Lifecycle-aware组件的最佳实践
- 使用Lifecycle-aware组件的场景
- 处理stop事件
- 其他资源
翻译自https://developer.android.com/topic/libraries/architecture/lifecycle 。
Lifecycle-aware
相关的组件能做出一些操作来响应其他构件1 中的状态变化,比如Activity
或者Fragment
。Lifecycle-aware
相关的组件能够帮助我们获得更好的代码结构,更少的代码,使代码更易维护。
一种常见的方式(构件状态监听的方式)是在Activity
和Fragment
的生命周期方法中实现依赖的操作。但是,这种方式导致不良的代码结构以及错误的增加。通过使用Lifecycle-aware
相关的组件,可以将依赖状态的代码从生命周期的方法中移除。
android.arch.lifecycle
包提供了类和接口,让我们可以构建根据Activity
或Fragment
的当前生命周期状态自动调整其行为的Lifecycle-aware
的代码。
Note: 在工程中引入
android.arch.lifecycle
,请看 adding components to your project。
Android框架中定义的大多数应用程序组件都附加了生命周期。生命周期由操作系统或运行在进程中的框架代码管理。它们是Android工作原理的核心,我们的应用程序必须遵守Android的工作原理。否则可能会触发内存泄漏甚至应用程序崩溃。
假如我们有一个Activity,在屏幕上显示设备位置。常见的实现可能如下所示:
class MyLocationListener { public MyLocationListener(Context context, Callback callback) { // ... } void start() { // connect to system location service } void stop() { // disconnect from system location service }}class MyActivity extends AppCompatActivity { private MyLocationListener myLocationListener; @Override public void onCreate(...) { myLocationListener = new MyLocationListener(this, (location) -> { // update UI }); } @Override public void onStart() { super.onStart(); myLocationListener.start(); // manage other components that need to respond // to the activity lifecycle } @Override public void onStop() { super.onStop(); myLocationListener.stop(); // manage other components that need to respond // to the activity lifecycle }}
即使这个例子看起来很好。但在真实的应用程序中,会有太多UI的管理和其他构件响应当前状态的生命周期的调用。管理多个构件会在生命周期方法中放置大量代码,例如onStart()
和onStop()
,这使得代码难以维护。
此外,无法保证组件在Activity
或Fragment
停止之前启动。如果我们需要执行长时间运行的操作则尤其如此,例如在onStart()
中的某些配置检查。这种情况会引起onStop()
方法(的业务操作)在onStart()
(的业务操作)之前完成,使得组件保持活动时间超过其所需的时间从而造成竞争条件。
class MyActivity extends AppCompatActivity { private MyLocationListener myLocationListener; public void onCreate(...) { myLocationListener = new MyLocationListener(this, location -> { // update UI }); } @Override public void onStart() { super.onStart(); Util.checkUserStatus(result -> { // what if this callback is invoked AFTER activity is stopped? if (result) { myLocationListener.start(); } }); } @Override public void onStop() { super.onStop(); myLocationListener.stop(); }}
android.arch.lifecycle
包提供了类和接口,可帮助我们以扩展性和隔离的方式解决这些问题。
Lifecycle
Lifecycle
是一个持有关于构件(比如activity或者fragment)的生命周期状态的信息,并且允许其它的对象观察构件状态的类。
Lifecycle
主要使用两个枚举来跟踪其关联的构件的生命周期状态。
-
Event
此生命周期的事件是通过框架和
Lifecycle
类来分发的。这些事件与Activity
和Fragment
中的事件回调相对应。 -
State
Lifecycle
对象跟踪的构件的当前的状态。
一个类可以通过在方法上添加注解来监听构件的声明周期的状态。然后通过调用Lifecycle
的 addObserver()
方法传递观察者的对象。如下:
public class MyObserver implements LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) public void connectListener() { ... } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) public void disconnectListener() { ... }}myLifecycleOwner.getLifecycle().addObserver(new MyObserver());
LifecycleOwner
LifecycleOwner
是一个单一的方法接口,表示该类具有Lifecycle
。它有一个方法getLifecycle()
,其实现类必须实现此方法。如果你正在尝试管理整个应用程序进程的生命周期,请参阅ProcessLifecycleOwner
。
此接口从各个类(如Fragment
和AppCompatActivity
)中抽象出生命周期的所有者,并允许编写与其一起使用的构件。任何自定义的应用的类都可以实现LifecycleOwner
接口。
实现LifecycleObserver
的构件与实现LifecycleOwner
的构件可以无缝协作,因为所有者提供生命周期,观察者可以注册观察。(解耦)。
对于位置跟踪的例子,我们可以让MyLocationListener
类实现LifecycleObserver
并且在activity的onCreate()
的方法中初始化它。这允许MyLocationListener
类自给自足,这意味着响应生命周期状态变化的逻辑在MyLocationListener
而不是activity中声明。各个构件存储自己的逻辑使得Activity
和Fragment
的逻辑更易于管理。
class MyActivity extends AppCompatActivity { private MyLocationListener myLocationListener; public void onCreate(...) { myLocationListener = new MyLocationListener(this, getLifecycle(), location -> { // update UI }); Util.checkUserStatus(result -> { if (result) { myLocationListener.enable(); } }); }}
一个常见的用例是,如果Lifecycle
现在不处于正常状态,则应避免调用某些回调。例如,如果回调在保存Activity
状态后运行Fragment
事务,则会触发崩溃,因此我们永远不会想要调用该回调。
为了简化这种情况,Lifecycle
允许其他的对象查询当前的状态。
class MyLocationListener implements LifecycleObserver { private boolean enabled = false; public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) { ... } @OnLifecycleEvent(Lifecycle.Event.ON_START) void start() { if (enabled) { // connect } } public void enable() { enabled = true; if (lifecycle.getCurrentState().isAtLeast(STARTED)) { // connect if not connected } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) void stop() { // disconnect if connected }}
通过此实现,我们的LocationListener
类完全可以识别生命周期。如果我们想要在其他的Activity
或者Fragment
中使用LocationListener
类,仅仅需要初始化就可以了。所有设置和拆卸操作都由类本身管理。
如果库提供了需要使用Android生命周期的类,建议使用生命周期Lifecycle-aware
的组件。库客户端可以轻松地集成这些组件,而无需在客户端进行手动生命周期的管理。
实现自定义LifecycleOwner
从support库26.1.0的版本开始,Fragment
和Activity
均实现了LifecycleOwner
接口。
如果有自定义类,想要实现LifecycleOwner
,可以使用LifecycleRegistry 类,但是需要在类中跟踪事件,代码如下:
public class MyActivity extends Activity implements LifecycleOwner { private LifecycleRegistry mLifecycleRegistry; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mLifecycleRegistry = new LifecycleRegistry(this); mLifecycleRegistry.markState(Lifecycle.State.CREATED); } @Override public void onStart() { super.onStart(); mLifecycleRegistry.markState(Lifecycle.State.STARTED); } @NonNull @Override public Lifecycle getLifecycle() { return mLifecycleRegistry; }}
Lifecycle-aware组件的最佳实践
-
保持UI控制器(Activity和Fragment)尽可能精简,UI控制器不应该获取数据,使用
ViewModel
去获取数据,并且观察LiveData
对象将变更映射到View上。 -
尽力去写数据驱动的UI,UI控制器的责任是当数据变更时更新View,或者用户操作通知
ViewModel
。 -
把数据逻辑放在
ViewModel
类中,ViewModel
应该充当UI控制器和应用程序其余部分之间的连接器。但需要注意,ViewModel
责任不是获取数据(例如从网络获取数据)。相反,ViewModel
应调用适当的构件来获取数据,然后将结果提供给UI控制器。 -
使用Data Binding来维护视图和UI控制器之间的简洁的接口。这可以使视图更具声明性,并最大限度地减少在
Activity
和Fragment
中所需的更新代码。如果使用Java编程语言执行此操作,使用Butter Knife
之类的库来避免样板代码并且具有更好的抽象。 -
如果UI很复杂,请考虑创建一个 presenter类来处理UI修改。这可能是一项艰巨的任务,但它可以使您的UI组件更容易测试。
-
避免在
ViewModel
中引用View
或Activity
上下文。如果ViewModel
周期大于Activity
周期2(在配置更改的情况下),Activity
将泄漏并且垃圾收集器不能回收此Activity
。
使用Lifecycle-aware组件的场景
Lifecycle-aware
组件在各种情况下都可以更轻松地管理生命周期。比如:
- 在粗粒度和细粒度位置更新之间切换。使用
Lifecycle-aware
组件在您的位置应用程序可见时启用细粒度位置更新,并在应用程序位于后台时切换到粗粒度更新。LiveData
一个Lifecycle-aware
组件,应用在用户位置变更时自动更新UI。 - 停止和开始视频缓冲,使用
Lifecycle-aware
组件尽可能地启动视频缓冲,但推迟播放直到应用程序完全启动。还可以使用Lifecycle-aware
组件在销毁应用程序时终止缓冲。 - 启动和停止网络连接。使用
Lifecycle-aware
组件在应用程序处于前台时启用网络数据的实时更新(流式传输),并在应用程序进入后台时自动暂停。 - 暂停和恢复动画drawables。当应用程序在后台时,使用
Lifecycle-aware
组件动画可绘制的内容,并在应用程序位于前台后恢复可绘制的内容。
处理stop事件
当Lifecycle
属于AppCompatActivity
或Fragment
时3,在调用AppCompatActivity
或Fragment
的onSaveInstanceState()
时,Lifecycle
的状态将更改为CREATED
,并且会派发ON_STOP
事件。(为什么要这么做,下面给出了解释。)
通过onSaveInstanceState()
保存Fragment
或AppCompatActivity
的状态时,在调用ON_START
事件之前,UI认为是不可变的。保存状态后尝试修改UI可能会导致应用程序的导航状态不一致。这就是为什么应用程序在保存状态后运行FragmentTransaction
,则FragmentManager
会抛出异常的原因。
如果观察者关联Lifecycle
不是处于 STARTED
或者之后的状态,LiveData
会通过禁止调用其观察者来防止这种边缘情况。此情况时,在决定调用其观察者之前可以调用isAtLeast()
方法。
不幸的是,在onSaveInstanceState()
之后才会调用AppCompatActivity
的onStop()
方法,在不允许UI状态更改(onSaveInstanceState()
之后)但是Lifecycle
尚未移到CREATED
状态(AppCompatActivity
的onStop()
方法尚未调用)的存在间隙。
为了防止这个问题,版本beta2及其更低的版本中,Lifecycle
类将状态标记为CREATED
,但没有派发事件,因此即使在系统调用onStop()
之前未派发事件,任何检查当前状态的代码都会获得实际值。
不幸的是,这个解决方案有两个主要问题 :
- 在API23及更低版本,Android系统实际上保存了活动的状态(调用了
onSaveInstanceState()
),即使它被另一个Activity
部分覆盖。换句话说,Android系统调用onSaveInstanceState
但它不一定调用onStop()
。这会创建一个潜在的长间隔,即使无法修改其UI状态,但观察者仍然认为生命周期处于活跃状态。 - 任何想要向
LiveData
暴露类似行为的类,都必须提供Lifecycle
版本 beta 2及更低版本提供的解决方法。
Notice : 为了简化流程并提供与旧版本的更好兼容性,从版本1.0.0-rc1开始,当
onSaveInstanceState()
调用时,不会等待onStop()
的调用,Lifecycle
对象标记为CREATED
,并且通知ON_STOP
。这不太可能影响代码,但需要注意这一点,它与API26及更低版本的Activity
类中的调用顺序不匹配。
其他资源
尝试使用Lifecycle
组件请看codelab。
Lifecyle-aware
组件是Android Jetpack的一部分,可以在Sunflower例子中看到时如何使用它们的。
我们通常说的组件为库,这里翻译成构件是指某个类,与组件不同。 ↩︎
长周期的对象持有短周期的对象,会导致短周期的对象泄露。 ↩︎
AppCompatActivity和Fragment属于support库中的类。 ↩︎
更多相关文章
- Android(安卓)GPS定位详解
- Android改变状态栏颜色真麻烦
- Android(安卓)关于判断应用是否有网络
- android activty的生命周期
- Android7.0 禁止锁屏状态的下拉状态栏
- Android(安卓)View 绘制流程之四:绘制流程触发机制
- Android(安卓)permission列表
- [AndroidTips]Android软件测试的日志文件抓取简介
- Android(安卓)TelephonyManager类的使用