Java 已调用Android TextView.setText();在Thread.sleep()阻塞之前返回,直到sleep()返回。为什么?

Java 已调用Android TextView.setText();在Thread.sleep()阻塞之前返回,直到sleep()返回。为什么?,java,android,multithreading,Java,Android,Multithreading,在Android框架中,如果调用了a的方法,并在其返回后调用,那么设备的屏幕在sleep()返回之前不会显示给定的文本。为什么? public class SleeperActivity extends Activity { public void onCreate(Bundle bundle) { super.onCreate(bundle); setContentView(R.layout.main); } public void d

在Android框架中,如果调用了a的方法,并在其返回后调用,那么设备的屏幕在
sleep()
返回之前不会显示给定的文本。为什么?

public class SleeperActivity extends Activity {
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(R.layout.main);
    }

    public void doDelay(View view) {
        try {
            TextView textView = (TextView) view;
            textView.setText("Sleeping");
            Log.d("Sleeper", "This log entry is invoked before sleeping");
            Thread.sleep(5000L);
            textView.setText("Ready");
        } catch (InterruptedException e) { finish(); }
    }
}
布局(未显示)有一个按钮,其
onClick
属性设置为
doDelay()

当编译并运行上述代码并单击按钮时,第一次调用
setText()
传递的文本直到线程休眠五秒钟后才会显示在屏幕上,即使日志条目在这五秒钟开始之前出现在日志中

为什么会发生这种情况,我应该怎么做才能在线程开始睡眠之前使第一次调用
setText()
时传递的文本显示在屏幕上?

1)在UI线程上从不睡那么久。事实上,永远不要在上面睡觉。这会导致它阻塞并使手机无响应

2) setText调用在视图上无效。这将导致框架尽快再次绘制视图,但这意味着需要完成当前的工作并返回到主循环器(基本上,它需要从它首先调用的代码中的任何方法返回)。在此之前,屏幕上不会显示更改

编辑:换句话说,在我们的框架内

do{
    Message msg = nextMessage();
    if(msg.what == ON_CREATE){
       currentActivity.onCreate();
    }
    else if(msg.what == DRAW){
        rootView.onDraw();
        //draw entire screen
    }
    else 
      ...  //Thousands of more events like touches, handlers, etc

在完成对当前消息的处理之前,它无法访问draw消息。它是单线程的。

观察到的行为的原因是,
视图
仅在通过事件循环的每个循环完成后重新绘制。android
onCreate()
方法以及触摸事件(如单击按钮)都由框架从事件循环中调用。因此,诸如
onCreate()
onClick()
之类的方法将在重新绘制任何
视图之前执行到完成,并且在这些方法返回之前,显示屏上不会有可见的效果。在这些方法中对
视图所做的更改只有在框架调用所述方法的事件循环周期完成后才会可见(
onCreate()
onClick()
,等等)

要实现问题中要求的行为,必须在事件循环循环完成后调用
sleep()
方法,在此循环中,您使用线程阻塞期间要显示的文本调用了
setText()
。一种方法是将对
sleep()
的调用替换为对
View.post()的调用
View.post()
采用
Runnable
方法,其
run()
方法将在事件循环的当前循环完成且框架显示了线程阻塞期间您希望看到的文本后被调用。通过在
Runnable
run()
方法中调用
Thread.sleep()
来更改问题中的
doDelay()
方法,如下所示:

    public void doDelay(View view) {
        final TextView textView = (TextView) view;
        textView.setText("Sleeping");
        textView.post(new Runnable() {
            public void run() {
                try {
                    Log.d("Sleeper", "Thread " + Thread.currentThread() + " going to sleep...");
                    Thread.sleep(5000L);
                    Log.d("Sleeper", "Thread " + Thread.currentThread() + " now awake");
                    textView.setText("Ready");
                } catch (InterruptedException e) { finish(); }
            }
        });
    }

我要感谢to,他的指导让我很轻松地回答了我自己的问题。

关于#1:我知道,谢谢。这只是一个我用来理解这种现象的教学例子。这不是生产代码。关于#2:我不理解你。我想你漏掉了一些词。“当前正在做的事情”是什么意思?“code it first calls”是什么意思?
setText()
显然是调用
sleep()
之前的“我的代码中它首先调用的方法”,并且该方法在调用
sleep()
之前返回。很明显我误解了你。你能给我展示一下对我给出的示例代码所做的必要的最小修改吗?在这个框架中,基本上有一个巨大的消息循环。该循环等待处理程序中的消息、IO事件、Android框架生成的事件(如onCreate)等。当您调用setText()时,它会向该循环发送一条绘制消息。直到循环可以处理该“绘制”命令,才会实际绘制视图。在onCreate函数完成之前,这不会发生。没有比这更快的方法了。什么是
onCreate()
阻止循环在完成之前处理DRAW命令的原因?我知道如果
sleep()
不在那里,显示文本不需要五秒钟,因此必须有一种方法(1)显示文本,(2)等待循环处理绘图,(3)按顺序调用
sleep()
。循环对DRAW命令的处理是否取决于(a)时间的流逝,或(b)onCreate()方法的完成?听起来你说的是(b),但你也用了“更快”这个词,这听起来像是一个时间问题。解决方法是什么?循环与onCreate在同一线程上执行。onCreate实际上由循环调用。因此,消息循环在返回到顶部的giant while语句之前不会请求下一条消息。在使用onCreate完成之前,它将无法执行此操作。也许我的编辑会让它更清晰。好吧,我很清楚什么不起作用,但我还是想知道什么起作用。如果有人雇用你编写一个Android应用程序,当它启动时:首先在屏幕上显示一些文本,然后一旦文本可见,就会在主线程上停留五秒钟,然后在五秒钟后在屏幕上显示一些其他文本,这可能吗?如果可能的话,那么您交付的代码与我发布的代码有何不同?