防止阻塞UI线程的一个好方法是让任务线程在后台运行。然而,当任务完成时,你常常需要更新UI。对于UI的更新只能由UI线程执行,否则将会产生异常。可以使用Handler类来做到这一点。Handler允许你发送在一段时间之后再由其处理的消息。这些消息可以立即进行处理,也可以计划好在将来的某段时间进行处理。Handler在handleMessage方法中处理消息。
默认情况下,一个Handler实例是绑定在创建它的线程上的(通常是主线程)。绑定Handler到UI线程上为异步更新UI提供了一个方便的方法。然而,你同样可以选择提供一个可选的Looper实例,让Handler运行在单独的线程上。循环类用于为一个线程运行消息循环。通过使用循环类,你可以发送消息并让它们运行于任何线程实例上。
注意:Looper类创建和管理一个包含单个线程的所有消息的MessageQueue对象。UI线程已经为你创建了一个消息队列和循环类。
Handler具有发送在一段时间之后再进行处理的消息的能力,这一点使得它非常适合实现基于时间的行为。
仅仅为了从一个后台线程中更新UI而创建一个Handler的情况很常见。Android提供了Activity.runOnUIThread方法,为这种情况提供了一条捷径。这个方法采用一个runnable并将其发送给UI线程的处理消息的Handler。主线程会在空闲时运行在runnable中的代码。
计时器实例
package com.example.testhandler; import android.support.v7.app.ActionBarActivity; import android.text.format.DateUtils; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startTimer();//启动计时器 } //========================= 以下为计时器代码 ======================= long mStart = 0; long mTime = 0; private Handler mHandler = new Handler() { //Handler回调函数将会默认在UI线程上执行,除非你明确地给出它会运行于另一个线程上。 public void handleMessage(Message msg) { long current = System.currentTimeMillis(); mTime += current - mStart; mStart = current; TextView counter = (TextView) MainActivity.this.findViewById(R.id.cdText); counter.setText(DateUtils.formatElapsedTime(mTime/1000)); mHandler.sendEmptyMessageDelayed(0, 250); } }; //启动 private void startTimer() { mStart = System.currentTimeMillis(); //为了阻止开启定时器两次,在发送下一条消息时移除任何存在的消息。 mHandler.removeMessages(0); mHandler.sendEmptyMessage(0); } //停止 private void stopTimer() { mHandler.removeMessages(0); } //重置 private void resetTimer() { stopTimer(); mTime = 0; } //检测定时器是否已经被停止 private boolean isTimerRunning() { return mHandler.hasMessages(0); } }
运行结果