感知生命周期 Lifecycles
教程来源
致谢B站良心Up主,教程地址戳这里
这次的例子我们将使用 “计时器” 控件来演示,先来了解一下计时器的用法。
计时器
计时器是属于 TextView 的子类,来到 Text 界面,直接将 TextView 改为 Chronometer 。
修改过后,界面便会出现一个时间,前面为分,后面是秒。我们将 id 改为 meter 。
计时器的常见方法
chronometer.setBase(SystemClock.elapsedRealtime()); //设置基准时间,并从该时间开始计数
// System.currentTimeMillis(); //(UNIX时间 自1970 1-1 到现在的毫秒数)
// SystemClock.elapsedRealtime(); //(系统启动到现在的时间,包含设备深度休眠的时间)
// SystemClock.uptimeMillis(); //(自系统启动时开始计数,毫秒为单位。从系统启动到现在,这个过程中的处于非休眠期的时间。当系统进入深度睡眠时(CPU关闭,设备变黑,等待外部输入装置)该时钟会停止)
chronometer.getBase(); //获取当前设置的基准时间
chronometer.setFormat("计时:%s"); //设置用于显示的格式化字符串(计时器将根据这个字符串来显示,替换字符串中第一个“%s”为当前"MM:SS"或 "H:MM:SS"格式的时间显示。)
chronometer.setCountDown(true); //设置为倒计时模式
chronometer.start(); //计时开始
chronometer.stop(); //停止刷新计时UI更新
前台计时,后台暂停的计时器
来到 MainActivity.java:
一般我们都将系统基准时间设为 SystemClock.elapsedRealtime(),这个时间是手机从上一次启动到现在经过的毫秒数,它只与系统启动有关,较为可靠,并且不设置的话,默认就是用它。
如果我们直接设置了基准时间(setBase)后,就开始计时(start),那么即使软件进入了后台,计时器不会停止,依旧在计时。
如果我们想要达到软件在前台计时,后台暂停的效果要怎么做呢?
生命周期中,当软件进入后台会默认调用 onPause(),
在这里,我们用变量 elapsedTime 保存下来了一个时间:
recordingTime = SystemClock.elapsedRealtime() - chronometer.getBase();
// 当前系统时间 - 启动计时器的时间 = 上次计时的持续时间,
我们在进入后台前,保存了上次计时的持续时间。
那么在我们再次回到前台时,会调用 onResume(),
此时,我们重新设置了基准时间:
chronometer.setBase(SystemClock.elapsedRealtime() - recordingTime );
// 本该是从0开始,我们跳过已经记录的时间,达到继续计时的效果。
public class MainActivity extends AppCompatActivity {
Chronometer chronometer;
private long recordingTime ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
chronometer = findViewById(R.id.meter);
chronometer.setBase(SystemClock.elapsedRealtime()); // 手机从上一次启动到现在,经过时间的毫秒数,比较可靠,只与系统启动有关,不写就默认为此
}
@Override
protected void onPause() {
super.onPause();
recordingTime = SystemClock.elapsedRealtime() - chronometer.getBase(); // 当前系统时间 - 启动时设定的起点时间 = 上次持续计数经过总时间
chronometer.stop();
}
@Override
protected void onResume() {
super.onResume();
chronometer.setBase(SystemClock.elapsedRealtime() - recordingTime );
chronometer.start(); // 开始计数
}
}
运行效果:
此时我们已经达到了前台计时,后台停止计时的计时器效果。
LifeCycles
上面我们虽然实现了计时器功能,但不是很好。我们将代码都放到了 MainActivity.java 中的生命周期中,如果对象少还好,如果对象多,大家都需要处理生命周期相关的行为,那么将会塞下很多代码,十分臃肿。并且这个代码的移植性几乎没有,为了优化这些问题,我们将使用 LifeCycles ,将这些功能放到 LifeCycles 中实现。
首先,创建一个 MyChronometer.java 文件,继承于 android.widget.Chronometer ,
创建完成后,默认报错,我们点击小灯泡,Create constructor matching super,
由于我们要将这个对象放到 xml 界面做编辑,我们需要 attrs 属性参数,如下选择,
MyChronometer.java :我们还需要让他实现一个接口,实现生命周期的观察。
public class MyChronometer extends Chronometer implements LifecycleObserver {
public MyChronometer(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
以上为 MyChronometer.java 的初始代码,接下来我们要将之前实现的前台计数,后台暂停的计数器功能在这里实现:
实现逻辑与之前一样,在进入后台前,将已经记录的时间保存下来,下次进入前台时,跳过已经记录的时间,实现了继续计时。
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) 是 LifecycleObserver 的特有写法,相当于在 MainActivity.java 中的 onPause() 代码块中写代码。由此可见,在实现了 LifecycleObserver 接口后,我们便可以在另一个类中自由的操纵生命周期了,增强了代码的移植性,并且减少了使得 MainActivity.java 中的代码更加简洁。
public class MyChronometer extends Chronometer implements LifecycleObserver {
private long recordingTime;
public MyChronometer(Context context, AttributeSet attrs) {
super(context, attrs);
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) // LifecycleObserver 特有写法
private void pauseMeter(){
recordingTime = SystemClock.elapsedRealtime() - getBase();
stop(); // 不写也可以
}
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
private void resumeMeter(){
setBase(SystemClock.elapsedRealtime() - recordingTime);
start();
}
}
在实现了计时器的生命周期后,我们需要去界面布局的 Text 中,将原本的 Chronometer 改为 MyChronometer。
然后来到 MainActivity.java,这里的代码将会变得十分简洁,关于生命周期的代码我们都不需要在这里实现了。我们在 MyChronometer 中实现了计时器的生命周期,在这里我们需要添加一句观察生命周期的代码:getLifecycle().addObserver(chronometer);
。
public class MainActivity extends AppCompatActivity {
MyChronometer chronometer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
chronometer = findViewById(R.id.meter);
getLifecycle().addObserver(chronometer);
}
}