Android Handler仅执行最后一次setText()及代码相关疑问
关于你的秒表代码的四个疑问解答
先把你的代码贴出来方便参考:
public class MainActivity extends AppCompatActivity { private boolean running = false; private int counter = 0; private Handler handler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); runTimer(); } public void onClickStart(View view){ running = true; } public void runTimer(){ final TextView textView = findViewById(R.id.timer); handler.post(new Runnable() { @Override public void run() { int hours = counter/3600; int minutes = (counter%3600)/60; int secs = counter%60; String time = String.format("%d:%02d:%02d", hours, minutes, secs); textView.setText(time); // Doesn't set it to this - see last line if(running){ counter++; } handler.postDelayed(this,1000); // what does happens between next one second textView.setText("hell0"); // Always set it to this } }); }
1. 为什么最初会跳过handler.post()?
这其实不是“跳过”,而是Handler的消息调度机制导致的正常现象。handler.post(Runnable)的作用是把这个Runnable对象放到主线程的消息队列中排队等待执行。而onCreate、onStart、onResume这些生命周期方法是主线程正在同步执行的任务,只有当这些当前任务全部执行完毕,主线程的Looper才会从消息队列里取出你post的Runnable去执行它的run()方法。所以流程看起来是先走完生命周期,再回到Runnable的run(),本质是消息排队的正常逻辑。
2. 为什么第一个textView.setText()没生效,只有最后一个起作用?
因为这两个setText()是在同一个run()方法里连续同步执行的。setText()会立即更新TextView的内容,但你刚把文本设为时间格式,紧接着又把它改成了"hell0",这两个操作几乎在瞬间完成,用户的眼睛根本捕捉不到中间的时间文本,视觉上就只看到最后设置的"hell0"。如果想让文本在两者之间切换,你需要给这两个操作加上时间间隔,比如用postDelayed分开执行,或者用定时器来交替切换。
3. postDelay()执行后的1秒延迟期间具体发生了什么?
handler.postDelayed(this, 1000)是告诉Handler:把这个Runnable对象放到消息队列里,等1000毫秒之后再执行它的run()方法。在这1秒的延迟期间,主线程完全是正常工作的——它可以处理用户的点击事件、刷新其他UI、处理系统发来的消息等等,不会被阻塞。这里的“延迟”只是让Runnable在消息队列里排队等待指定时间,而不是让主线程休眠或者暂停运行代码。
4. postDelay(this,1000)为何使用this而非this.run()?
这里的this指的是当前的Runnable实例,而postDelayed方法需要的参数是一个Runnable对象——它会把这个对象放入消息队列,在延迟时间到了之后再调用它的run()方法。
如果换成this.run(),那就是直接同步调用run()方法,相当于你在当前run()方法里又递归调用了自己,这会立即触发无限递归,导致主线程卡死,完全达不到“延迟1秒再执行”的效果。所以必须传入Runnable对象本身,而不是直接调用它的run()方法。
内容的提问来源于stack exchange,提问作者lynxx

