handler笔记(sendXXpostXX延迟处理)
Posted 夜尽天明89
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了handler笔记(sendXXpostXX延迟处理)相关的知识,希望对你有一定的参考价值。
关于handler的post、延迟消息这2个点有点忘记了。这两天重看了下handler源码,学习笔记总结下
之前写过一篇,handler源码分析
这里只说下架 post、延迟消息。
测试代码很简单:(Kotlin)
private var mHandler = object : Handler()
override fun handleMessage(msg: Message?)
super.handleMessage(msg)
Log.e("收到消息 $msg?.what","时间:$System.currentTimeMillis()")
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mHandler.sendEmptyMessage(1)
mHandler.sendEmptyMessageDelayed(2,5*1000)
mHandler.post(Runnable()
Log.e("handler","post 线程:$Thread.currentThread()")
)
mHandler.postDelayed(
Log.e("handler","postDelayed 线程:$Thread.currentThread()")
,10*1000)
对应日志:
2021-04-06 09:15:07.237 E/收到消息 1: 时间:1617671707237
2021-04-06 09:15:07.237 E/handler: post 线程:Thread[main,5,main]
2021-04-06 09:15:12.204 E/收到消息 2: 时间:1617671712204
2021-04-06 09:15:17.204 E/handler: postDelayed 线程:Thread[main,5,main]
结论:
1、利用post发消息,可以直接跟回调处理代码
2、post完成后,处理代码的是主线程
postXX方法,最后也会都走到 sendMessageAtTime
引申个问题,handler 是怎么保证,延迟操作的正确性的?或者说,如果我指定了90秒后打印一句话,在90面之内,我改了手机系统时间,还会正常触发打印吗?答案是肯定的,会正常打印。是怎么保证延迟时间的正确性的?看下源码:
sendEmptyMessageDelayed、postDelayed 这些延迟代码
最后都会走到
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis)
if (delayMillis < 0)
delayMillis = 0;
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
可以看到2点:
1、如果我们制定了延迟操作的时间是负数。即:假设现在是10点,我要在9点发个消息,是无法实现的,延迟时间,最后都会被处理成非负数
2、延迟时间,前面加了个 SystemClock.uptimeMillis() ,这个明显不是时间戳。
进去看下
/**
* 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();
也就是说,用handler发出的延迟消息,是加在 手机启动时间(单位:毫秒) 上的,和手机的系统时间无关。这样的话,不管怎么改手机显示时间,也不会影响延迟消息的正确,当然,深度睡眠情况要单独考虑,不过在现实中,也几乎用不到深度睡眠情况
操作验证:
将代码改成:(send的延迟改成90秒)
mHandler.sendEmptyMessage(1)
mHandler.sendEmptyMessageDelayed(2,90*1000)
mHandler.post(Runnable()
Log.e("handler","post 线程:$Thread.currentThread()")
)
mHandler.postDelayed(
Log.e("handler","postDelayed 线程:$Thread.currentThread()")
,10*1000)
启动APP后(上午9点多),切换到系统设置里,把时间改成未来时间,我是改成了下午1点多。查看日志:
2021-04-06 09:17:40.831 E/收到消息 1: 时间:1617671860831
2021-04-06 09:17:40.831 E/handler: post 线程:Thread[main,5,main]
2021-04-06 09:17:50.842 E/handler: postDelayed 线程:Thread[main,5,main]
2021-04-06 13:19:02.348 E/收到消息 2: 时间:1617686342348
因为改了手机系统时间,时间戳也变了。如果算下 “收到消息”后面的时间戳差值,是4个多小时。但是根据其他表的观察,“收到消消息” 1和2的时间差,近90秒。
再次启动(这个时候,手机时间是未来时间),切换到系统设置,把时间改回来。查看日志:
2021-04-06 13:19:47.238 E/收到消息 1: 时间:1617686387238
2021-04-06 13:19:47.238 E/handler: post 线程:Thread[main,5,main]
2021-04-06 13:19:58.112 E/handler: postDelayed 线程:Thread[main,5,main]
2021-04-06 09:21:27.356 E/收到消息 2: 时间:1617672087356
可以看到,时间差对的上。
注:handler的延迟操作,不一定的正好的指定时间差,会有略微的偏差。即:发送消息1,延后5秒后发送消息2,1和2的差值,不一定是正好的5秒
接下来,需要单独说下 postAtTime 这个方法。这个方法是特别的,所有我上面才会说:
1、postXX方法,最后也会都走到 sendMessageAtTime
2、sendEmptyMessageDelayed、postDelayed 这些延迟代码,最后都会走到 sendMessageDelayed。
先看下源码:
/**
* Causes the Runnable r to be added to the message queue, to be run
* at a specific time given by <var>uptimeMillis</var>.
* <b>The time-base is @link android.os.SystemClock#uptimeMillis.</b>
* Time spent in deep sleep will add an additional delay to execution.
* The runnable will be run on the thread to which this handler is attached.
*
* @param r The Runnable that will be executed.
* @param uptimeMillis The absolute time at which the callback should run,
* using the @link android.os.SystemClock#uptimeMillis time-base.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the Runnable will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis)
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
多余的翻译,我就不复制了,就说2个单词:
specific:特殊的,特定的;明确的;详细的
absolute:绝对的
这个方法,有2个特别的地方:
1、时间是特定的,需要明确指定;2、没有延迟时间判负情况。
注意到了吗?之前的方法,会有延迟时间小于0时,置为0的操作。这里没有,也就是说,传进来负数,也会被直接使用
测试代码:
mHandler.postAtTime(
val sdf = SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒")
Log.e(
"handler 延迟 5",
"postAtTime $sdf.format(Date(System.currentTimeMillis())) ; 线程:$Thread.currentThread()"
)
, 5 * 1000)
//--------------------------
mHandler.postAtTime(
val sdf = SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒")
Log.e(
"handler 延迟 -5",
"postAtTime $sdf.format(Date(System.currentTimeMillis())) ; 线程:$Thread.currentThread()"
)
, -5 * 1000)
//--------------------------
mHandler.postAtTime(
val sdf = SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒")
Log.e(
"handler 手机启动时间延迟 5",
"postAtTime $sdf.format(Date(System.currentTimeMillis())) ; 线程:$Thread.currentThread()"
)
, SystemClock.uptimeMillis() + 5 * 1000)
//--------------------------
mHandler.postAtTime(
val sdf = SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒")
Log.e(
"handler 手机启动时间延迟 -5",
"postAtTime $sdf.format(Date(System.currentTimeMillis())) ; 线程:$Thread.currentThread()"
)
, SystemClock.uptimeMillis() - 5 * 1000)
日志:
2021-04-06 10:27:20.597 E/handler 延迟 -5: postAtTime 2021年04月06日 10时27分20秒 ; 线程:Thread[main,5,main]
2021-04-06 10:27:20.597 E/handler 延迟 5: postAtTime 2021年04月06日 10时27分20秒 ; 线程:Thread[main,5,main]
2021-04-06 10:27:20.598 E/handler 手机启动时间延迟 -5: postAtTime 2021年04月06日 10时27分20秒 ; 线程:Thread[main,5,main]
2021-04-06 10:27:25.569 E/handler 手机启动时间延迟 5: postAtTime 2021年04月06日 10时27分25秒 ; 线程:Thread[main,5,main]
我是在 27分20秒的时候启动的APP,触发打印日志。(这里多说句:视觉上,打印日志会比界面展示快一点,这是对的,因为界面绘制需要时间,打印日志,是onCreate里直接打印的)。
由此可得出结论:
1、调用 postAtTime ,需要自己明确指定时间;
2、时间可以传负数(可以的意思是:传负数 不报错、不崩溃)
3、延迟时间的基础,必须是基于 APP启动时间
4、如果传了错误时间(负数时间、在启动时间上减去一个时间),会立刻走回调方法,延迟操作不起任何作用
最后:在上面的代码中,我尝试把 “handler 手机启动时间延迟 5”这段代码的延迟时间改成了 System.currentTimeMillis() + 5 * 1000。启动后,就再没看到日志。应该是延迟时间非常长了(个人猜测,请自行验证)。
以上是关于handler笔记(sendXXpostXX延迟处理)的主要内容,如果未能解决你的问题,请参考以下文章
Web Api 2 Controller 和 Handler 之间有 5 秒的延迟