kotlin 在主线程上睡眠导致视图更改被跳过?

ubof19bj  于 2023-06-24  发布在  Kotlin
关注(0)|答案(2)|浏览(153)

我想在我的应用程序中添加一个红色闪光灯,当某些事件发生时。使用windowManager,我创建并添加了一个简单的视图,它只是用红色填充画布,并将不透明度初始化为%
为了实现“闪光”,我计划将不透明度设置为100%,休眠25ms,然后将不透明度设置回0%
我知道我可以让它正常工作,方法是

view.alpha = 255f
handler.postDelayed(
  Runnable {
    view.alpha = 0f
  }, 25)

但是,当我尝试直接在主线程上使用Thread.sleep()添加延迟时,我对这种行为感到困惑

view.alpha = 255f
Thread.sleep(25)
view.alpha = 0f

这基本上使得第一个语句view.alpha = 255f永远不会执行。
然而,如果我把这段代码放在后台线程中,那么行为就会像我预期的那样工作

Thread(
                Runnable {
                  view.alpha = 255f
                  Thread.sleep(50)
                  visualizer.alpha = 0f
                })
            .start()

我的问题
1.为什么在休眠主线程之前视图的改变没有被执行(或者至少是不可见的)?
1.为什么我可以在后台线程上更改视图的不透明度?这不被认为是视图更改,需要从UI线程完成吗?

t1qtbnec

t1qtbnec1#

下面是Android绘图系统的工作原理:
当您更改视图时,它会向主线程发布一条无效消息。当处理该消息时,调用视图onDraw。onDraw创建一组绘制命令,然后由另一个线程处理,将它们绘制到屏幕上。
如果主线程休眠,它将永远不会返回到事件循环顶部的处理程序。如果不这样做,它就永远不会处理无效消息。所以它从来不画。
为什么会这样呢?好吧,假设你想改变一个视图的背景色,文本和文本颜色。如果立即绘制对视图的更改,则需要3次昂贵的绘制。通过发送INVALIDATE消息,它允许将所有3个更改合并为1个绘制,从而提高效率。这也是地球上几乎所有操作系统的工作方式。
所以基本上-永远不要暂停主线程。如果这样做,在主线程循环器重新启动之前,您不会看到绘制和其他命令的结果。
另外,postDelayed的示例不会发生在另一个线程上。默认情况下,处理程序在创建它们的线程上运行,该线程可能是主线程,除非您真实的努力使其不存在。如果你试图在另一个线程上运行一个处理程序,并从那个线程更改UI,你会崩溃。处理程序通过将消息发送到消息队列来工作。主线程的消息队列是由操作系统创建的,它与用于发送绘图无效消息的队列相同

4xrmg8kj

4xrmg8kj2#

因为主线程正在使用一个LOOPER,其中绘图和布局是步骤之一。
当您更改视图属性时,它们将触发无效(请求重画)或请求布局(更新其大小和位置)。
这意味着“一次”对多个视图的更改本质上是在下一个布局或绘制步骤中批处理并一起执行的,因此UI线程不会因多次执行这些代价高昂的操作而不堪重负。它还以适当的顺序保存了许多与ui生命周期相关的回调和事件。
因此,考虑到这一点:
1:通过调用view.alpha = 0,然后调用view.alpha = 255,零改变永远不会被执行,因为主循环器在绘制步骤中从未看到它。你在主线程上添加Thread.sleep实际上是一个违规行为,因为你冻结了你的应用程序- looper不能向前移动,因此没有绘图被执行。
2:除了不是所有视图属性/方法都强制执行主线程调用之外,没有太多要说的。我想说大多数影响视图层次结构的那些都是这样的。

相关问题