Android主线程进行 Thread.Sleep()会导致ANR吗

震惊!真正引起ANR的并不是所谓的耗时操作,而是......

首先 先 明白一个问题:什么是ANR

Application Not Responding,意思是”应用没有响应“

以前我的理解就是 “在主线程做了耗时操作”就会引起ANR,现在我觉得我是错误的,ANR的意思是应用没有响应,耗时操作实际上 并不一定会导致没有响应,我对没有响应的理解是

有人(事件或操作)发出了一个请求,但是主线程没有对这个人进行反馈(可能是没时间、可能是不想理、可能是手被绑住了没有办法理你),这个叫没有响应

那举个例子

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // do some blablabla...
  
        Log.d("test", "准备sleep30秒")
        Thread.sleep(30000)
        Log.d("test", "sleep30秒完成")

        // do some blablabla...

    }

这段代码在 onCreate 中 sleep 了 30秒,会出现 ANR 吗?

答案是

可能会,也可能不会

当主线程在 Sleep 的时候,如果 UI线程不需要进行操作,也就是说没有消息会发送给UI线程并要求UI线程进行处理的时候 Sleep 30秒就不会导致ANR,因为没有 出现 ANR(应用没有响应)的情况啊,没有人向线程请求什么东西,也就不需要响应了,既然没有响应了,那怎么会有ANR呢?

但是,但线程在Sleep的时候,主线程有接收到需要处理的请求的时候

需要注意的是,需要处理的请求,不一定只是用户的手动触摸,也有可能是其他线程需要对线程进行UI更新的请求,这个时候UI线程正在Sleep,根本没有办法理你(不想理你),这就符合了ANR的条件,所以会出现ANR(比如说在这 30 秒内,点击了 返回按钮,就会出现 ANR)

另外,如果在这个30秒内进行UI更新会发生ANR吗?

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        Handler().postDelayed(Runnable {
            Log.d("sleep_test", "准备更新text")
            btn_test.text = "update btn text"
            Log.d("sleep_test", "更新text完成")
        }, 10000)

        Log.d("sleep_test", "准备sleep30秒")
        Thread.sleep(30000)
        Log.d("sleep_test", "sleep30秒完成")

        Log.d("sleep_test", "first update")
        btn_test.text = "This is the first update"
}

输出结果为:

D/sleep_test: 准备sleep30秒
D/sleep_test: sleep30秒完成
D/sleep_test: first update
D/sleep_test: 准备更新text
D/sleep_test: 更新text完成

并没有发生ANR,这是因为这段代码里面的sleep休眠了线程,代码里面的更新操作根本没有在 sleep的时候被触发(处于休眠状态),也就没有了发送请求的前提条件,所以并没有发生ANR。但是如果用户手动进行了触摸操作,相当于有一个请求的事件了,而主线程又被休眠了,超过了规定的时间就会触发ANR提示

在android里面都对其进行了常量定义

Android N 的 ANR时间

  • Service 超时
// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20*1000; // 前台

// How long we wait for a service to finish executing.
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10; // 后台
  • Broadcast 超时
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000;  // 前台
static final int BROADCAST_BG_TIMEOUT = 60*1000;  // 后台
  • InputDispatching 超时
 // How long we wait until we timeout on key dispatching.
 static final int KEY_DISPATCHING_TIMEOUT = 5*1000;
  • ContentProvider 超时
// How long we wait for an attached process to publish its content providers
// before we decide it must be hung.
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;

ANR的学习目前就到这个点,后续还要继续学习

延伸:Thread.sleep() 还有其加锁的问题

由于CPU分配的每个线程的时间片极为短暂(一般为几十毫秒),所以,CPU通过不停地切换线程执行,这样就给程序员一种错觉,以为多个线程是在同时执行。sleep就是正在执行的线程主动让出CPU,CPU去执行其他线程,在sleep指定的时间过后,CPU才会回到这个线程上继续往下执行。

 
comments powered by Disqus