文章目录
- 一、搭建布局
- 二、非 Lifecycle 实现
- 三、Lifecycle 实现
- 四、Demo 效果
一、搭建布局
新建 LifecycleDemo
工程实现 界面停留时间计时,在 activity_main.xml
搭建简单布局(省略属性),如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout tools:context=".MainActivity">
<Chronometer android:id="@+id/chronometer" />
</androidx.constraintlayout.widget.ConstraintLayout>
仅有一个控件:
chronometer
- Chronometer,计时器。
整体布局如下:
二、非 Lifecycle 实现
在 MainActivity
类中,重写 onResume()
和 onPause()
如下:
class MainActivity : AppCompatActivity() {
private var elapsedTime: Long = 0L
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onResume() {
super.onResume()
// 更新计时器 base 时间
chronometer.base = SystemClock.elapsedRealtime() - elapsedTime
chronometer.start()
}
override fun onPause() {
super.onPause()
// 记录 Activity onPause 时已经经过时间
elapsedTime = SystemClock.elapsedRealtime() - chronometer.base
chronometer.stop()
}
}
可以举一个直观的例子理解一下:
系统启动 | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
SystemClock.elapsedRealtime() | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 |
① Activity onResume | ▶ | ||||||||||||||
① chronometer.base | 03 | ||||||||||||||
② elapsedTime | 0 | 1 | 2 | 3 | 4 | ||||||||||
② Activity onPause | !! |
- 在系统启动 3s 后启动
Activity
,此时设置Chronometer
的base
为当前系统启动后经过的时间,即 3s; Activity
前台运行 4s 后退到后台,此时经过的时间elapsedTime = SystemClock.elapsedRealtime() - chronometer.base
,即7 - 3 = 4s
。
系统启动 | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
SystemClock.elapsedRealtime() | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 |
① Activity onResume | ▶ | ||||||||||||||
② elapsedTime | 0 | 1 | 2 | 3 | 4 | ||||||||||
② chronometer.base | 03 | → | 05 |
Activity
退到后台之后又经过 2s,SystemClock.elapsedRealtime()
走到了 9s,此时将Activity
拉起到前台;- 要保证计时器数字不变,需要调整它的
base
与当前系统经过时间的差值不变,即调整base
为 5s,chronometer.base = SystemClock.elapsedRealtime() - elapsedTime
,即9 - 4 = 5s
。
系统启动 | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
SystemClock.elapsedRealtime() | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 |
① chronometer.base | 05 | ||||||||||||||
① elapsedTime | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
- 此时
Activity
在前台,Chronometer
可以按照调整后的base
正常计时。
效果如下:
三、Lifecycle 实现
上面示例看起来没什么问题,但在真实的应用中,会有很多界面管理和组件调用,在 onXXXX()
生命周期回调中放置大量的代码,会变得难以维护。因此需要以弹性和隔离的方式解决这些问题,Lifecycle
组件提供了这样的方式,所以可以尝试使用 Lifecycle
重新实现上面示例。
首先,自定义一个计时器控件 MyChronometer
继承自 Chronometer
并实现 LifecycleObserver
接口:
class MyChronometer @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : Chronometer(context, attrs, defStyleAttr), LifecycleObserver {}
通过向 MyChronometer
类中的方法添加注解来监控组件的生命周期状态,方法内具体逻辑和上面类似:
class MyChronometer @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : Chronometer(context, attrs, defStyleAttr), LifecycleObserver {
private var elapsedTime: Long = 0L
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
private fun resumeChronometer() {
base = SystemClock.elapsedRealtime() - elapsedTime
start()
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
private fun pauseChronometer() {
elapsedTime = SystemClock.elapsedRealtime() - base
stop()
}
}
修改 activity_main.xml
中使用自定义计时器控件 MyChronometer
:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout tools:context=".MainActivity">
<com.example.lifecycledemo.MyChronometer android:id="@+id/chronometer" />
</androidx.constraintlayout.widget.ConstraintLayout>
在 MainActivity
中通过调用 Lifecycle
类的 addObserver()
方法并传递观察者的实例来添加观察者:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycle.addObserver(chronometer)
}
}
四、Demo 效果
效果如下: