当前位置: 首页 > article >正文

关于安卓Handler之延时我不准时

背景

最近在做一个小功能,其中涉及到一个延时处理逻辑,如果是通过定时去轮询并且执行,那么就会导致一个耗电问题,所以定时轮询是不实际的,所以暂停的思路,就是通过延时实现。

思考

安卓延时,好家伙,一看还能有几个能实现,第一个handler,第二个AlarmManager,还有一些什么thread pool interval之类的。本文只会讲述,Handler实现,其他后续有机会再讲解。

开发环境

win10,as4+,jdk8+

过程

文末将会放出,所有关联的代码
要点

有小伙子可能会发现,哟,这个不是手到拈来吗,直接handler postDelay就行了啊,何必再浪费口舌?的确,这种思路上是没有任何问题,有问题的是,系统的cpu会休眠,如果handler delay的时间过长,那么就会导致时间不准。

举个例子,如果业务上面,需要延时10个小时,直接用handler的delay实现,你会发现百分之九十九都是不准的。一般情况,都会延时个几分钟,甚至十几分钟。导致这种原因,就是cpu休眠以及应用冻结等方面的影响,具体源码不再一一列出了。

还有一种场景,有一个业务,handler做定时器,一秒执行一次,如果手机机型了锁屏,正常情况,handler也会失效,这也和系统的休眠机制有关系。所以,这里就涉及到一个结束时间的概念,要以结束时间为准,而不是算好delay的次数为准。

实现

那么,有了以上的问题,应该要怎么去实现,怎么去避免?目前有一个大概的思路,就是把延时的时间,就行一个“阶梯划分”。
举例:如果你要延时一个小时执行,那么就不能直接delay一个小时,我们需要划分为,先delay个半小时,然后半小时到了,再计算剩余时间,然后再delay个十五分钟,同样地,最后粒度越来越小,这种就能够保证大部分情况下,延时的准确性,当然,这是大部分情况。博主实测,应用挂后台24小时,延时任务依旧按时执行。

这个逻辑,虽然有点麻烦,但是按道理是实用的。不过这种实现方式,不适用于对时间要求很高的场景。

下面是全部关联的代码,博主把它们封装成为一个类了:

package com.example.demo.utils;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import androidx.annotation.NonNull;

import java.io.Serializable;

/**
 * FileName: HandlerDelayer
 * Author: lzt
 * Date: 2024/10/31 10:06
 * Description:延时工具类
 */
public class ModuleTimerHandlerDelayer implements Serializable {

    private static final String TAG = ModuleTimerHandlerDelayer.class.getSimpleName();
    private final Handler mDelayHandler;
    private DelayCallback mDelayCallback;
    //延时阈值
    private static final long DELAY_THRESHOLD = 2 * 1000 * 60;
    //多线程锁
    private final Object SYNC_LOCK = new Object();
    //是否结束了
    private boolean isRelease = false;

    public ModuleTimerHandlerDelayer() {
        mDelayHandler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                Object messageObj = msg.obj;
                if (!(messageObj instanceof DelayClass)) {
                    return;
                }
                DelayClass delayClass = (DelayClass) messageObj;
                if (!delayClass.isSegment()) {
                    //不用切割,直接回调
                    if (getDelayCallback() != null) {
                        getDelayCallback().runTask(delayClass.getMessage());
                    }
                    return;
                }
                //需要切割,重新切割
                long endTime = delayClass.getEndTime();
                long nextDelay = getDelayTime(endTime);
                if (nextDelay <= 0) {
                    if (getDelayCallback() != null) {
                        getDelayCallback().runTask(delayClass.getMessage());
                    }
                    return;
                }
                //存在延时,继续delay
                Message obtMessage = mDelayHandler.obtainMessage();
                obtMessage.what = delayClass.messageWhat;
                obtMessage.obj = delayClass;
                mDelayHandler.sendMessageDelayed(obtMessage, nextDelay);
            }
        };
    }

    private String createMessageKey() {
        return String.valueOf(System.currentTimeMillis());
    }

    public DelayCallback getDelayCallback() {
        return mDelayCallback;
    }

    public Message getMessage() {
        return mDelayHandler.obtainMessage();
    }

    /**
     * 传入结束时间,计算delay
     */
    private long getDelayTime(long endTime) {
        if (System.currentTimeMillis() >= endTime) {
            //已经结束了
            return 0L;
        }
        long interval = endTime - System.currentTimeMillis();
        if (interval > 1000 * 60 * 60L * 2L) {
            //大于两小时
            return 1000 * 60 * 60L;
        } else if (interval > 1000 * 60 * 30L) {
            //大于三十分钟
            return 1000 * 30L;
        } else if (interval > 1000 * 60 * 10L) {
            //大于十分钟
            return 1000 * 10L;
        } else if (interval > 1000 * 60L) {
            //大于一分钟
            return 1000 * 20L;
        } else {
            return interval;
        }
    }


    /**
     * 延时包装类
     */
    private static class DelayClass implements Serializable {
        private final int messageWhat;
        private final long startTime;
        private final long endTime;
        private final long delayTime;
        private final Message message;
        private final String key;

        //是否为分段延时
        private boolean isSegment = false;

        private DelayClass(String key, int messageWhat, long startTime, long endTime, long delayTime, Message message) {
            this.messageWhat = messageWhat;
            this.key = key;
            this.startTime = startTime;
            this.endTime = endTime;
            this.delayTime = delayTime;
            this.message = message;
        }

        public boolean isSegment() {
            return isSegment;
        }

        public void setSegment(boolean segment) {
            isSegment = segment;
        }

        public String getKey() {
            return key;
        }

        public long getEndTime() {
            return endTime;
        }

        public int getMessageWhat() {
            return messageWhat;
        }

        public long getStartTime() {
            return startTime;
        }

        public long getDelayTime() {
            return delayTime;
        }

        public Message getMessage() {
            return message;
        }
    }


    public interface DelayCallback {
        void runTask(Message message);
    }


    //外部回调--------------------------------------------------------------------

    public void setDelayCallback(DelayCallback delayCallback) {
        mDelayCallback = delayCallback;
    }


    //外部调用--------------------------------------------------------------------

    public void delay(long delay, Message message) {
        if (isRelease) {
            return;
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (SYNC_LOCK) {
                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    String messageKey = createMessageKey();
                    Log.d(TAG, "delay start messageKey: " + messageKey);
                    int what = message.what;
                    long endTime = System.currentTimeMillis() + delay;
                    DelayClass delayClass = new DelayClass(messageKey, what, System.currentTimeMillis(), endTime, delay, message);
                    if (delay <= DELAY_THRESHOLD) {
                        //直接发送
                        delayClass.setSegment(false);
                        Message obtMessage = mDelayHandler.obtainMessage();
                        obtMessage.what = delayClass.messageWhat;
                        obtMessage.obj = delayClass;
                        mDelayHandler.sendMessageDelayed(obtMessage, delayClass.delayTime);
                        return;
                    }
                    //分段延时
                    delayClass.setSegment(true);
                    Message obtMessage = mDelayHandler.obtainMessage();
                    obtMessage.what = delayClass.messageWhat;
                    obtMessage.obj = delayClass;
                    long delayTime = getDelayTime(endTime);
                    mDelayHandler.sendMessageDelayed(obtMessage, delayTime);
                }
            }
        }).start();
    }


    public void emptyDelay(int what, long nextTaskDelayTime) {
        synchronized (SYNC_LOCK) {
            String messageKey = createMessageKey();
            long endTime = System.currentTimeMillis() + nextTaskDelayTime;

            Message orgMessage = getMessage();
            orgMessage.what = what;

            DelayClass delayClass = new DelayClass(messageKey, what, System.currentTimeMillis(), endTime, nextTaskDelayTime, orgMessage);
            delayClass.setSegment(true);
            Message obtMessage = mDelayHandler.obtainMessage();
            obtMessage.what = delayClass.messageWhat;
            obtMessage.obj = delayClass;
            mDelayHandler.sendMessageDelayed(obtMessage, delayClass.delayTime);
        }

    }

    public void release() {
        isRelease = true;
        mDelayHandler.removeCallbacksAndMessages(null);
    }

}

代码解析

对于上面代码核心的思路,就是通过一个wrapper,进行包装外部传入的message,同时把结束时间也带上,方便后续的结束时间重新计算。聪明的你们,估计一看代码,就会恍然大悟了。

注意,用完以后,记得release哦

that’s all---------------------------------------------------


http://www.kler.cn/a/377596.html

相关文章:

  • mutable用法
  • 论文阅读-用于点云分析的自组织网络
  • 微服务架构面试内容整理-领域驱动设计(DDD)
  • 全志A133 android10 LVDS幅值调节
  • CAN总线学习笔记(1、CAN总线定义)
  • 鸿蒙进阶-AlphabetIndexer组件
  • Nginx 报错400 Request Header Or Cookie Too Large
  • 【MogDB】MogDB5.2.0重磅发布第九篇-SQL宽容性提升
  • npm入门教程7:npm语义化版本控制
  • Flink CDC 同步 Mysql 数据
  • 今日 AI 简报|多智能体协作平台、全能 AI 音频生成、长文本生成框架等前沿 AI 技术与应用
  • 【.NET 8 实战--孢子记账--从单体到微服务】--简易权限--接口路径管理
  • K 临近算法
  • AJ-Report:一款开源且非常强大的数据可视化大屏和报表工具
  • Nginx 深度解析:高性能 Web 服务器与反向代理的艺术
  • Hcia知识汇总
  • 局部加权回归
  • 【MySQL】 运维篇—安全管理:数据加密与SSL配置
  • 快消零售行业的培训创新:构建在线培训知识库
  • Apache Hive分布式容错数据仓库系统
  • 函数理解(c)
  • 1009:带余除法
  • Windows 安全日志解析
  • 飞桨首创 FlashMask :加速大模型灵活注意力掩码计算,长序列训练的利器
  • 程序的全生命周期
  • vue3 keep-alive简单说明