从上一节的内容可以看到,handler负责发送消息到android内部提供的消息队列中,然后在一定的时机从消息队列将消息取出进行处理。出于安全性和偶合性的考虑,handler并不直接与消息队列进行交互,而上通过一个名为Looper的类来负责交互。
上一节讲解的handler的用法全部都上在主线程上进行的。如果我们需要处理比较耗时的事情,就不能把handler放在主线程中了。
线程在默认的时候是没有Looper与之相关联。在线程中,我们可以通过调用prepare 方法来启动一个消息loop,调用loop方法来通知Looper来处理消息,直至结束。Looper提供了很多的静态方法来与线程、消息队列进行交互。一个线程最多允许创建一个Looper。
以下是SDK文档中介绍的在线程中使用handler的一种方法:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
有一个特例, 当应用程序的进程创建时,主线程会启动一个消息队列和Looper,并通过该队列管理一些优先级别比较高的对象(如Activity 等)。
考虑以下情况,定义一个全局变量mHandler:
mRunnable = new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
String s = mHandler.getLooper().getThread().getName();
Log.e("minrui", s);
mHandler.postDelayed(mRunnable, 1000);
}};
Thread t = new Thread(mRunnable,"malone");
t.start();
我们创建一个名为“malone”的线程,并启动,在其Runnable接口的run方法中通过handler发送消息,并打印出与该handler相关联的looper所在的线程的名字。
打印结果如下:
ERROR/minrui(1630): main
结果似乎有点出人意料,但是仔细分析,启动的名为“malone”的线程并未与一个looper相关联,并且handler中的looper是与主线程关联的,结果确实应该是这样。
那么,可不可以在主线程中定义handler,在代码中动态改变handler使之与其他线程的looper相关联呢?看以下的例子:
mRunnable = new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
Looper.prepare();
mHandler = new Handler(Looper.myLooper());
String s = mHandler.getLooper().getThread().getName();
Log.e("minrui", s);
mHandler = new Handler(Looper.getMainLooper());
s = mHandler.getLooper().getThread().getName();
Log.e("minrui", s);
}};
Thread t = new Thread(mRunnable,"malone");
t.start();
结果打印如下:
ERROR/minrui(4049): malone
ERROR/minrui(4049): main
Android提供了一个包含looper的thread类,这个类的名字叫HandlerThread,我们看一下这个类的用法:
t = new HandlerThread("malone123");
t .start();
mHandler1 = new Handler(t.getLooper());
mHandler1.post(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
String s = mHandler1.getLooper().getThread().getName();
Log.e("minrui", s);
}
});
打印结果如下:
ERROR/minrui(3528): malone123
有一点值得注意的是:
在android2.1之前,android认为在非线程中操作UI界面是不安全的,因此禁止在其它线程中修改UI界面。解决的方法有两种,一是通过在主线程中定义的handler更新界面,二是直接调用被修改的view的postInvalidate方法刷新单个view。