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

时间:2013-05-16 19:07:11

标签: java android multithreading

在Android框架中,如果调用TextViewsetText()方法,并且在调用后返回Thead.sleep(),则设备的屏幕不会显示直到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()中传递的文本出现在屏幕上?

2 个答案:

答案 0 :(得分: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

在完成当前消息之前,它无法进入绘制消息。它的单线程。

答案 1 :(得分:1)

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

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

Runnable

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