【IT168技术】在案例程序代码中用到了一个android.os.Handler类的对象—handler,android.os.Handler是Android中多个线程间消息传递和计划任务的“工具”类。Handler会在多个线程之间发送Message、执行Runnable。使用这些类可以对运行在不同线程中的多个任务进行排队。
Handler工具类在多线程中有两方面的应用:
发送消息,在不同的线程间发送消息,使用的方法为sendXXX();
计划任务,在未来执行某任务,使用的方法为postXXX()。
1、发送消息
android.os.Handler对象通过下面的方法发送消息的:
l sendEmptyMessage(int),发送一个空的消息;
sendMessage(Message),发送消息,消息中可以携带参数;
l sendMessageAtTime(Message, long),未来某一时间点发送消息;
l sendMessageDelayed(Message, long),延时Nms发送消息。
一个线程发出消息后,另外的线程要接收消息,接收消息通过重写Handler类的handleMessage(Message)方法实现,如下代码所示:
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
labelTimer.setText("逝去了 " + msg.obj + " 秒");
}
}
};
2、计划任务
android.os.Handler对象通过下面的方法执行计划任务:
l post(Runnable),提交计划任务马上执行;
l postAtTime(Runnable, long),提交计划任务在未来的时间点执行;
l postDelayed(Runnable, long),提交计划任务延时Nms执行。
通过Handler的postXXX()方法提交计划任务,那么谁来完成这个任务呢?可以通过实现Runnable接口的run()方法:
public void run() {
... ...
}
};
代码清单8-7所示是把案例修改成计划任务方式的代码,完整代码请参考chapter8_6工程中 chapter8_6代码部分。
【代码清单8-7】
private String TAG = "chapter8_6";
private Button btnEnd;
private TextView labelTimer;
private boolean isRunning = true;
private Handler handler;
private int timer = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnEnd = (Button) findViewById(R.id.btnEnd);
btnEnd.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
isRunning = false;
}
});
handler = new Handler();
Runnable r = new Runnable() {
public void run() {
if (isRunning) {
labelTimer.setText("逝去了 " +timer + " 秒");
timer++;
handler.postDelayed(this, 1000);
}
}
};
handler.postDelayed(r, 1000);
labelTimer = (TextView) findViewById(R.id.labelTimer);
}
}
运行一次handler.postDelayed(this, 1000) 只能向线程提交一次消息,如果想反复运行线程体(run()方法),就要在run()方法的后面再次提交,这样就会达到每1s执行一次线程体(run()方法)的效果了。
发送消息和计划任务有什么不同?发送消息可以向一个线程通信请求,请求它执行某些处理,并可以携带数据,这些数据被封装在Message对象中,发送消息的线程处理方法就是重写handleMessage()方法,代码如下:
@Override
public void handleMessage(Message msg) {
... ...
}
};
计划任务也可以向一个线程通信发请求,请求它执行某些处理,但不可以携带数据,计划任务的线程处理方法就是重写run()方法,代码如下:
public void run() {
... ...
}
};
消息发送和计划任务提交之后,它们都会进入目标线程的消息队列中,目标线程并不区分是主线程还是子线程。图8-12所示是消息发送示意图,线程2就是目标线程。图8-13所示是计划任务提交示意图,线程2就是目标线程,计划任务也是有消息队列维护的。
▲图8-12 消息发送
▲图8-13 计划任务
无论是消息发送还是计划任务都可以实现“计时器”案例,使用那一种方式可以根据自己的喜好选择。但是由于UI控件labelTimer是在主线程中声明和创建的,因此更新labelTimer操作必须在主线程中处理,也就是说更新UI的主线程必须作为目标线程。
▲图书参考