android 定时器
程序员文章站
2022-06-09 15:01:43
...
android 定时器 (奇葩需求)
先说说这个定时器滴需求
- 定时(这个就是废话了)
- 当页面有定时器时,不管是否是destory了,只要时间没到,当再次进入这个页面时,不能重新计时。比如 A 进入 B 页面 ,在B页面开启定时器(假设60s)后, 回到了A页面 ,10秒后再次进入B ,这时,B页面显示的倒计时为50s(漂亮滴产品小姐姐想出来的需求,汝等屌丝也只能奋力实现啦。。。)。
- 另外一点,貌似是还需要区分账号,不同滴账号分别计时(只要把上面的实现了,这个就比较好做了,后面会讲到思路)
思路
目前一共有三种实现方式(区别在于计时器的实现方式不同),我实现了前面两种方式,第三种的话,就靠大家自己实现了。而且我也封装了一下,只需要改变实例化的对象,就可以在两种计时方式中随意切换了。
1、全局使用一个计时器,将页面的key与时间保存在一个map中,通过计时器去回调(后面会讲到实例代码)
2、利用一个全局的arraylist,将页面的key与计时器保存在一个map中,在将map放入list里面。(计时器通过实现CountDownTimer来做滴)
3、 将页面的key与开始时间保存在一个map中,然后页面自己持有一个计时器,自己处理。
代码实现
第一种方式
先看接口类(ITimer)
public interface ITimer {
/**
* 绑定回调
* @param name 页面的key
* @param onNotifyTimeCallback 回调
*/
void bind(String name, OnNotifyTimeCallback onNotifyTimeCallback);
/**
* 任务开始的时候调用
* 默认情况下返回59~0
* @param name 页面的key
* @param onNotifyTimeCallback 回调
*/
void start(String name, OnNotifyTimeCallback onNotifyTimeCallback);
/**
* 任务开始的时候调用
* 默认情况下返回59~0
* @param name 页面的key
* @param onNotifyTimeCallback 回调
* @param millisInFuture 总时间
* @param countDownInterval 间隔时间
*/
void start(String name, OnNotifyTimeCallback onNotifyTimeCallback, long millisInFuture, long countDownInterval);
/**
* 移除绑定
* @param name 页面的key
*/
void unbind(String name);
}
这个是实现类(TimerHelper )
``java
public class TimerHelper implements ITimer {
//保存页面时间
private Map<String, Long> mMap;
private Timer mTimer;
private ArrayList<Map<String, OnNotifyTimeCallback>> mCallbacks = new ArrayList<>();
private static TimerHelper timerHelper;
//默认时间
private static final long MILLIS_IN_FUTURE = 60 * 1000;
private static final long COUNT_DOWN_INTERVAL = 1000;
private long millisInFuture;
private long countDownInterval;
/**
* 定时timer
*/
private TimerTask mTimerTask;
/**
* TimerHelper 单例
* @return
*/
public static TimerHelper getInstance() {
if (timerHelper == null) {
synchronized (TimerHelper.class) {
if (timerHelper == null) {
timerHelper = new TimerHelper();
}
}
}
return timerHelper;
}
private TimerHelper() {
mMap = new HashMap<>();
}
@Override
public void start(String name, OnNotifyTimeCallback onNotifyTimeCallback) {
start(name, onNotifyTimeCallback, MILLIS_IN_FUTURE, COUNT_DOWN_INTERVAL);
}
@Override
public void start(String name, OnNotifyTimeCallback onNotifyTimeCallback,
long millisInFuture, long countDownInterval) {
this.millisInFuture = millisInFuture;
this.countDownInterval = countDownInterval;
putKey(name);
Map<String, OnNotifyTimeCallback> map = new HashMap<>();
map.put(name, onNotifyTimeCallback);
mCallbacks.add(map);
onNotifyTimeCallback.onNotify(-1L);
}
/**
* 将页面的key与开始时间存入map中,并且启动timer
* @param name
*/
private void putKey(String name) {
if (mMap.isEmpty()) {
mMap.put(name, System.currentTimeMillis());
startTimer();
} else {
if (!mMap.containsKey(name)) {
mMap.put(name, System.currentTimeMillis());
}
}
}
/**
* 如果map中有key 则返回time,如果没有则加入map中
* 这个方法会在onresume中或者onstart中调用
* 目的是为了如果有计时,则重新接收回调
* 因为回调会在onpause或者onstop中被移除
* @param name
* @return 回调-1 表示需要重新开始计时(比如页面第一次打开,或者当前计时完成,需要重新计时)
*/
@Override
public void bind(String name, OnNotifyTimeCallback onNotifyTimeCallback) {
if (mMap.containsKey(name)) {
long time = millisInFuture - (System.currentTimeMillis() - mMap.get(name));
if (time <= millisInFuture) {
Map<String, OnNotifyTimeCallback> map = new HashMap<>();
map.put(name, onNotifyTimeCallback);
mCallbacks.add(map);
onNotifyTimeCallback.onNotify(time / 1000);
}
} else {
onNotifyTimeCallback.onNotify(-1L);
}
}
/**
* 当页面不在可见时,就不需要接收回调
* 直接移除
* @param name 页面的key
*/
@Override
public void unbind(String name) {
for (int i = 0; i < mCallbacks.size(); i++) {
if (mCallbacks.get(i).containsKey(name)) {
mCallbacks.remove(i);
return;
}
}
}
private void startTimer() {
mTimer = new Timer();
mTimerTask = new MyTimeTask();
mTimer.schedule(mTimerTask, 0, countDownInterval);
}
/**
* 轮训检查
* 轮训时间按照自己设定的间隔
* 这里默认是1000ms
*/
private void check() {
if (mMap != null && mMap.isEmpty()) {
mTimerTask.cancel();
mTimerTask = null;
mTimer.cancel();
return;
}
sendMessage();
scheduleMap();
}
/**
* 每次轮训都会给当前页面发送回调
* 帮助view发生变化
*/
private void sendMessage() {
if (mCallbacks.size() != 0) {
//只会有一个页面有回调
Map<String, OnNotifyTimeCallback> map = mCallbacks.get(mCallbacks.size() - 1);
Iterator<Map.Entry<String, OnNotifyTimeCallback>> iterator = map.entrySet().iterator();
if (iterator.hasNext()) {
Map.Entry<String, OnNotifyTimeCallback> entry = iterator.next();
OnNotifyTimeCallback callback = entry.getValue();
long time = (millisInFuture - (System.currentTimeMillis() - mMap.get(entry.getKey()))) / 1000;
callback.onNotify(time);
}
}
}
/**
* 每次轮训检查超时的任务
* 然后将超时任务移除
* 第一步将当前key的map对象移除
* 第二步将当前key的回调移除
*/
private void scheduleMap() {
long currentTime = System.currentTimeMillis();
Iterator<Map.Entry<String, Long>> it = mMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Long> entry = it.next();
if ((currentTime - entry.getValue()) >= millisInFuture) {
mMap.remove(entry.getKey());
for (Map<String, OnNotifyTimeCallback> map : mCallbacks) {
if (map.containsKey(entry.getKey())) {
mCallbacks.remove(map);
break;
}
}
}
}
}
class MyTimeTask extends TimerTask {
@Override
public void run() {
check();
}
}
}
第二种实现
public class TimerHelper1 implements ITimer {
private Map<String, TimeTask> mMap;
private static TimerHelper1 instance;
private static final long MILLIS_IN_FUTURE = 60 * 1000;
private static final long COUNT_DOWN_INTERVAL = 1000;
//写这个builder是为了方便单元测试滴时候去mock对象
private TimeTaskBuilder timeTaskBuilder;
//默认构造器
private TimerHelper1() {
mMap = new HashMap<>();
timeTaskBuilder = new TimeTaskBuilder() {
@Override
public TimeTask build(String tag, long millisInFuture, long countDownInterval) {
return new TimeTask(millisInFuture, countDownInterval, tag);
}
};
}
public static TimerHelper1 getInstance() {
if (instance == null) {
synchronized (TimerHelper1.class) {
if (instance == null) {
instance = new TimerHelper1();
}
}
}
return instance;
}
public static void setupTimeTaskBuilder(TimeTaskBuilder builder) {
instance.timeTaskBuilder = builder;
}
/**
* onResume或者onstart方法中调用
* @param str 页面的key
* @param onNotifyTimeCallback 回调
*/
@Override
public void bind(String str, OnNotifyTimeCallback onNotifyTimeCallback) {
if (mMap.containsKey(str)) {
TimeTask timeTask = mMap.get(str);
timeTask.setOnNotifyTimeCallback(onNotifyTimeCallback);
onNotifyTimeCallback.onNotify(timeTask.getMillis());
}
}
@Override
public void start(String str, OnNotifyTimeCallback onNotifyTimeCallback) {
start(str, onNotifyTimeCallback, MILLIS_IN_FUTURE, COUNT_DOWN_INTERVAL);
}
/**
* 任务启动调用
* @param name 页面的key
* @param onNotifyTimeCallback 回调
* @param millisInFuture 总时间
* @param countDownInterval 间隔时间
*/
@Override
public void start(String name, final OnNotifyTimeCallback onNotifyTimeCallback,
long millisInFuture, long countDownInterval) {
final TimeTask timeTask = timeTaskBuilder.build(name, millisInFuture, countDownInterval);
timeTask.setOnNotifyTimeCallback(new OnNotifyTimeCallback() {
@Override
public void onNotify(long time) {
if (time == 0) {
mMap.remove(timeTask.tag);
}
Log.d("DEBUG_DEBUG", mMap.size() + "");
onNotifyTimeCallback.onNotify(time);
}
});
mMap.put(name, timeTask);
timeTask.start();
}
/**
* 移除回调
* @param string
*/
@Override
public void unbind(String string) {
if (mMap.containsKey(string)) {
mMap.get(string).setOnNotifyTimeCallback(null);
}
}
public static class TimeTask extends CountDownTimer {
private long millis;
private String tag;
public void setOnNotifyTimeCallback(OnNotifyTimeCallback onNotifyTimeCallback) {
mOnNotifyTimeCallback = onNotifyTimeCallback;
}
public OnNotifyTimeCallback getOnNotifyTimeCallback() {
return mOnNotifyTimeCallback;
}
private OnNotifyTimeCallback mOnNotifyTimeCallback;
/**
* @param millisInFuture The number of millis in the future from the call
* to {@link #start()} until the countdown is done and {@link #onFinish()}
* is called.
* @param countDownInterval The interval along the way to receive
* {@link #onTick(long)} callbacks.
*/
public TimeTask(long millisInFuture, long countDownInterval, String str) {
super(millisInFuture, countDownInterval);
this.millis = millisInFuture;
this.tag = str;
}
/**
* 每隔一秒(默认)会进行回调
* @param millisUntilFinished
*/
@Override
public void onTick(long millisUntilFinished) {
millis = millisUntilFinished / 1000;
if (mOnNotifyTimeCallback != null) {
mOnNotifyTimeCallback.onNotify(millis);
}
}
/**
* 倒计时完成后回调
*/
@Override
public void onFinish() {
if (mOnNotifyTimeCallback != null) {
mOnNotifyTimeCallback.onNotify(0);
}
}
public long getMillis() {
return millis;
}
}
public interface TimeTaskBuilder {
TimeTask build(String tag, long millisInFuture, long countDownInterval);
}
}
第三种实现方式就大家自己做了
下面写一下调用方式
1、 实现OnNotifyTimeCallback接口并在onCreate中实例化对象
ITimer mITimer;
mITimer = TimerHelper.getInstance();
2、 在onResume或者onStart中bind回调
@Override
protected void onResume() {
super.onResume();
mITimer.bind(getLocalClassName(), this);
}
3、 在onPause或者onStop中unbind回调
@Override
protected void onPause() {
super.onPause();
mITimer.unbind(getLocalClassName());
}
4、在需要的事件中发起任务(我这里写入的是倒计时10s,每隔1s执行,也可以不用穿这两个参数,默认为60s,每隔1s执行)
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mITimer.start(getLocalClassName(), MainActivity.this, 10000, 1000);
}
});
5、在onNotify里面执行更新操作
@Override
public void onNotify(long time) {
remainTime = time;
runOnUiThread(new Runnable() {
@Override
public void run() {
if (remainTime == -1L || remainTime == 0L) {
button.setText("10");
button.setClickable(true);
button.setBackgroundColor(Color.GREEN);
} else {
button.setText(remainTime + "");
button.setClickable(false);
button.setBackgroundColor(Color.GRAY);
}
}
});
}
如果要切换实现方式 直接在 mITimer = TimerHelper.getInstance();这里选择要实现的方式就好了,如果还有其他方式,直接实现ITmer接口,然后实现那几个方法就好了。