3

在 Android 框架中,如果调用 aTextViewsetText()方法,并且在它返回之后Thead.sleep()被调用,那么设备的屏幕不会显示给定的文本,直到 aftersleep()已经返回。为什么?

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()线程开始休眠之前出现在屏幕上?

4

2 回答 2

3

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

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

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

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

在处理完当前消息之前,它无法获取绘图消息。它的单线程。

于 2013-05-16T19:11:03.217 回答
2

观察到的行为的原因是Views 仅在通过事件循环的每个循环完成后重绘。androidonCreate()方法以及诸如单击按钮之类的触摸事件由框架从事件循环中调用。因此,诸如onCreate()or之类的方法onClick()将在任何 s 被重绘之前执行完成View,并且在这些方法返回之前不会对显示产生可见的影响。在这些方法中对Views 所做的更改只有在框架调用所述方法的事件循环周期完成后才会变得可见(onCreate()onClick()等)

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

    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(); }
            }
        });
    }

我要感谢Gabe Sechan,他的指导让我无法回答我自己的问题。

于 2013-05-17T15:42:23.890 回答