Android开发常见问题:为什么不要用Timer?

news2024/9/28 21:21:56

前言

从刚开始做Android开始,就时刻谨记一条规则:如果有能实现功能的Android API,就不要用Java API。所以我很少用Timer,直到前一阵一个朋友问我:

我有一个Timer计时器,我看logcat已经启动了,为什么没执行呢,跟修改了时钟有关系吗?

当时简单的看了一下源码,发现Timer确实和时钟有关系,让他修改成Handler,昨天又想起来这个问题觉得应该好好整理一下。

正文

首先写一个Demo:

// 点击启动Timer计时器
findViewById(R.id.start_timer).setOnClickListener(v -> startTimer());
// 点击启动Handler延时任务
findViewById(R.id.start_handler_delay).setOnClickListener(v -> startHandlerDelay());

/**
* 10s后,修改TextView显示Handler delay成功
*/
private void startHandlerDelay() {
	Log.e("lzp", "start handler delay");
    mHandler.postDelayed(() -> {
        mTextView.setText("start handler delay successfully");
        Log.e("lzp", "start handler delay successfully");
    }, 10 * 1000);
}

/**
* 10后,修改TextView显示Timer delay成功
*/
private void startTimer() {
	Log.e("lzp", "start timer");
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            mTextView.setText("start timer successfully");
            Log.e("lzp", "start timer successfully");
        }
    }, 10 * 1000);
}

为了验证修改时钟是否会影响delay任务,所以延迟10s,在10s内修改模拟器时钟,看看验证的结果。
第一步:Handler delay中修改时钟快1个小时:
在这里插入图片描述

第二步:Handler delay中修改时钟慢一个小时:
在这里插入图片描述
第三步:Timer delay中修改时钟快一个小时:
在这里插入图片描述
第四步:Timer delay中修改时钟慢一个小时:
在这里插入图片描述
从测试的情况看,修改时钟对于Handler实现的delay无影响,对于Timer实现的delay如果是把时钟调慢,Timer不会按时执行。

Handler delay的实现

public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
	return sendMessageDelayed(getPostMessage(r), delayMillis);
}

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

/**
 * Returns milliseconds since boot, not counting time spent in deep sleep.
 *
 * @return milliseconds of non-sleep uptime since boot.
 */
@CriticalNative
native public static long uptimeMillis();

上面postDelayed方法内部的实现,最终使用的是SystemClock.uptimeMillis(),从该方法的注释可以看出这个方法返回的是开机时间(不包含深度休眠的时间),所以修改了时钟对于Handler无影响。

Timer的delay实现

public void schedule(TimerTask task, long delay) {
    if (delay < 0)
        throw new IllegalArgumentException("Negative delay.");
    sched(task, System.currentTimeMillis()+delay, 0);
}

Timer的schedule使用的是System.currentTimeMillis(),这个方法返回的是当前时间戳,所以修改了时钟,必然会执行的效果有影响,所以还得看一下具体的Task执行的源码:

private void mainLoop() {
    while (true) {
        try {
            TimerTask task;
            boolean taskFired;
            synchronized(queue) {
                ...
                task = queue.getMin();
                synchronized(task.lock) {
                    ...
                    // 当前时间戳
                    currentTime = System.currentTimeMillis();
                    // task执行的时间戳,就是刚刚调用schedule算出的: System.currentTimeMillis()+delay
                    executionTime = task.nextExecutionTime;
                    // task执行时间小于等于当前时间戳,taskFired为true
                    if (taskFired = (executionTime<=currentTime)) {
                        if (task.period == 0) { // Non-repeating, remove
                            queue.removeMin();
                            task.state = TimerTask.EXECUTED;
                        } else { // Repeating task, reschedule
                            queue.rescheduleMin(
                              task.period<0 ? currentTime   - task.period
                                            : executionTime + task.period);
                        }
                    }
                }
                // taskFired为false,表示task执行时间未到,需要wait对应的时间差
                if (!taskFired) // Task hasn't yet fired; wait
                    queue.wait(executionTime - currentTime);
            }
            // 执行task
            if (taskFired)  // Task fired; run it, holding no locks
                task.run();
            ...
    }
}

为了方便理解,这里删除了部分与分析无关的代码,简单的说Timer内部有一个死循环,不停的取队列中时间最小的任务,然后对比当前时间是否已经大于了task的执行时间,如果已经大于会执行task,否则等待要执行的时间差。

所以当时钟调快一个消失,时间戳必然大于task的执行时间,所以task可以正常执行,但是当调慢了以后,当前时间戳会小于task的执行时间,相当于delay的时间被延长了时钟变慢的时间。例如:在delay期间,修改时钟慢一分钟,那么task就会在一分钟后执行,下面是logcat截图:
在start timer之后修改时钟慢一分钟

总结

从实现上对比,其实Handler和Timer的实现是类似的:队列 + 任务 + 处理器。在性能上可能不会太大的差别。Handler的实现更适合做延时任务,Time更适合做定时器,例如服务端的定时任务,例如每天晚上的0点备份数据,节假日开启某个配置等等。

最后总结一句话:在Android开发,Timer几乎所有的使用场景都有对应的Android API可以替换,所以非特殊情况,还是别用比较好。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/139405.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式应该从哪里入手?

就从目前IT行业大的方向上来讲嵌入式工程师相对来讲是非常吃香的&#xff0c;学习嵌入式开发的朋友数量也是非常的多的&#xff0c;那么对于对嵌入式工程师我们应该从哪里入手呢? 下面就根据我自身多年的一个实际的工作经验来给哪些不知道怎么入门的朋友来简单的介绍一下。 前…

C语言进阶——指针(一)

目录 一. 字符指针 二. 指针数组 三. 数组指针 四. 数组参数、指针参数 1.一维数组传参 2.二维数组传参 3.一级指针传参 4.二级指针传参 一. 字符指针 在之前&#xff0c;我们就了解到过字符指针 int main() {char aW;//字符变量char* pa&a;//字符指针*…

1.3日报

今天优化了getMobileByScene接口&#xff0c; 测试accountadd接口 遇到的问题与解决 在升级安装python3时&#xff0c;由于操作失误&#xff0c;导致系统错误&#xff0c;在同事帮助下重装了系统&#xff0c;好在保住了文件。但是软件还得重新配置。 getMobileByScene接口在…

精益|什么是价值流图分析(VSM)?

在精益生产管理中&#xff0c;价值流研究主要是指利用制作价值流图&#xff0c;进行价值流图研究可以发现并消除浪费、降低成本&#xff0c;改进企业运营状况&#xff0c;提升企业竞争力。因此进行价值流研究离不开价值流图。 价值流图&#xff08;Value Stream Mapping&#x…

基于KT6368A蓝牙芯片开发智能抖音翻页翻页笔总结

一、功能简介 KT6368A蓝牙芯片也是基于 HID 开发&#xff0c;主要用于浏览当下火爆的抖音等小视频的上下翻页、左右菜单切换、暂停等操作。打开手机蓝牙进行连接&#xff0c;进入视频浏览界面操作对应按键即可。包含一个蓝牙的指示灯&#xff0c;表示是否连接 。同时支持adkey按…

HBase面试题汇总

1、请描述HBse的&#xff1f; 答&#xff1a; Memstore级别&#xff1a;当MemStore的大小达到设置阈值&#xff08;默认128M&#xff09;&#xff0c;会触发flush操作。 1、HBase中Memstore在何时进行数据的flush操作&#xff1f; 答&#xff1a; Memstore级别&#xff1a…

uniapp 填坑之旅---udb微信小程序端显示异常

功能描述&#xff1a;A页面展示列表a&#xff0c;点击a&#xff0c;进入B页面&#xff0c;展示a对象关联的子对象b。在B页面中&#xff0c;通过unicloud-db组件manual模式加载&#xff0c;具体代码按照官网示例来写。问题描述&#xff1a;代码实现后&#xff0c;一直在H5调试&a…

Jmeter安装配置使用超详细教程(亲测有效)

文章目录1、Jmeter下载2、JDK安装3、Jmeter环境部署4、验证jmeter5、修改语言6、接口测试1、Jmeter下载 1.1、下载地址 http://jmeter.apache.org/download_jmeter.cgi 1.2、选择对应版本&#xff0c;本文以windows版本为例&#xff0c;版本号为5.5。 2、JDK安装 jmeter安装…

到底为什么那么多大厂在开始疯狂裁员?

最近几年大家都听到了好多大厂公司开始裁员&#xff0c;比如鹅厂、狗厂、鸟厂、熊厂等。 接下来给大家讲个故事&#xff0c;希望故事看完&#xff0c;你就会懂了&#xff01; 外国的神父呆了不久 留下几个 P 就走了&#xff0c; 一个 P 叫 BPR&#xff0c; 一个 P 叫 ERP。 …

作业1/4 设备树总结

1.什么是设备树 设备树&#xff08;Device Tree)是用来描述&#xff08;存储&#xff09;硬件信息的一种树形结构&#xff0c;设备树在linux内核启动的时候传递给内核被内核解析&#xff0c;用来描述设备信息的一种方式&#xff08;地址&#xff0c;中断号...&#xff09;。设备…

《收获,不止Oracle》表的设计之五朵金花

表设计主要强调什么场合该选择什么技术&#xff0c;没有最高级的技术&#xff0c;只有最适合的技术。 1.表的特性 普通堆表的不足之处 1.查看产生多少日志 [oracleoracle-db-19c ~]$ sqlplus / as sysdbaSQL*Plus: Release 19.0.0.0.0 - Production on Wed Jan 4 14:27:13 20…

电商数据监测的应用价值——国内吸尘器行业数据浅析

随着科技的发展与居民生活水平的提高&#xff0c;吸尘器进入日常生活&#xff0c;成为了常见家用清洁用具之一。2022年上半年&#xff0c;吸尘器市场零售额达145亿元&#xff0c;同比增速达14.0%&#xff0c;零售量1008万台&#xff0c;同比下跌2.8%。&#xff08;数据来源&…

Hadoop HA高可用

文章目录Hadoop HA高可用1.1 HA概述1.2 HDFS-HA工作机制1.2.1 HDFS-HA工作要点1.2.2 HDFS-HA自动故障转移工作机制1.3 HDFS-HA集群配置1.3.1 环境准备1.3.2 规划集群1.3.3 配置Zookeeper集群1&#xff09;集群规划2&#xff09;解压安装3&#xff09;配置zoo.cfg文件4&#xff…

药品需求加大,蒸汽发生器等制药设备该如何快速有效地进行维护?

一、行业背景药品的生产与制造的每一道关卡都是十分严格的&#xff0c;一方面&#xff0c;涉及到化工污染问题&#xff0c;制药的废气一旦没有得到妥善处理&#xff0c;则会危及到周边居民以及企业工作人员的健康。另一方面&#xff0c;药品本着治病救人的原则&#xff0c;其品…

[Android]序列化原理Serializable

引入 我们知道&#xff0c;当一个程序终止时&#xff0c;这个程序创建的对象也会随着程序终止&#xff0c;那么我需要如何做才能不受其他程序的状态影响并且可以得到其他程序创建的对象状态呢&#xff1f;这时候我们就可以使用Serializable来进行序列化把对象持久化到存储设备上…

【服务器数据恢复】断电导致服务器故障的数据恢复案例

服务器数据恢复环境&#xff1a; 某品牌服务器&#xff0c;12块硬盘组成raid5磁盘阵列&#xff0c;存储普通文件。 服务器故障&#xff1a; 机房供电不稳定导致服务器断电&#xff0c;管理员重启服务器后发现服务器无法正常使用。 根据用户描述&#xff0c;北亚服务器数据恢复工…

使用vscode在CMake工程中集成gtest共享库进行单元测试

使用vscode在CMake工程中集成gtest共享库做单元测试一、概述二、工程内容清单三、CMakeLists.txt内容说明四、构建工程一、概述 本文主要介绍如何在一个多层次目录结构的CMAKE工程中以共享库的形式集成gtest进行单元测试。 关于如何使用CMake管理多层次目录结构的CMake工程&a…

FFmpeg 解复用实战

1.封装格式相关函数 avformat_alloc_context()&#xff1a;负责申请一个AVFormatContext结构的内存,并进行简单初始化&#xff0c;这个函数可以不用手动调用&#xff0c;内部会自动调用。 avformat_free_context()&#xff1a;释放该结构里的所有东西以及该结构本身&#xff0…

再学C语言24:分支和跳转——逻辑运算符和条件运算符

一、多重选择else if 功能&#xff1a;在两个以上的语句中作出选择 示例代码&#xff1a; #include <stdio.h> int main(void) {float score;printf("Please enter your score: \n");scanf("%f", &score);if(score < 60)printf("不及…

SSM基础整合

SSM基础整合 文章目录SSM基础整合[toc]SSM基础整合SSM整合流程表现层数据封装设置统一数据返回结果类定义code类优化Controller优化后的返还结果异常处理器异常处理项目异常处理方案项目异常分类处理SSM基础整合 SSM整合流程 创建工程 SSM整合 Spring SpringConfig Configur…