android 消息处理机制

Handler、Looper、MessageQueue

handler在创建的时候,会采用当前线程的looper来构造消息循环系统。handler内部通过ThreadLocal来获取当前线程的Looper。线程默认是没有Looper的,如果需要使用handler,就必须为线程创建Looper。主线程(UI线程),它是ActivityThread,被创建时会初始化Looper,这也是在主线程中默认可以使用handler的原因。如果当前线程中没有Looper而试图使用handler来收发消息,就会报错。

Handler通过sendMessage或者post()等系列方法将消息发送到消息队列(MessageQueue)中,Looper不断轮询消息队列,如果发现有新的消息,则处理,如果没有新的消息,则阻塞。

MessageQueue:
消息队列主要包含两个操作:插入和读取。读取本身伴随着删除操作。插入和读取对应的方法分别为:enQueueMessage和next。MessageQueue虽然叫消息队列,但内部的实现方式并非队列,而是单链表。那么插入和读取消息也就是对单链表的插入和读取。next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里。当有新消息到来时,next方法会返回这条消息并将其从单链表中移除。
for (;;) {
   
if (nextPollTimeoutMillis != 0 ) {
        Binder.
flushPendingCommands();
    }

   
nativePollOnce(ptr, nextPollTimeoutMillis);

   
synchronized ( this ) {
       
// Try to retrieve the next message.  Return if found.
       
final long now = SystemClock.uptimeMillis();
        Message prevMsg =
null ;
        Message msg =
mMessages ;
       
if (msg != null && msg. target == null ) {
           
// Stalled by a barrier.  Find the next asynchronous message in the queue.
           
do {
                prevMsg = msg;
                msg = msg.
next ;
            }
while (msg != null && !msg.isAsynchronous());
        }

Looper的工作原理:
Looper在android的消息机制中扮演着消息循环的角色,具体来说就是它会不停地从Messageq中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在那里。
可以通过Looper.perpare()方法为当前线程创建一个Looper,通过Looper.loop()来开启消息循环。
Looper可以也是可以退出的,Looper提供quit和quitSafely来退出一个Looper,两者的区别是:quit会直接退出Looper,二quitSafely只是设定一个退出标记,然后把消息队列中已有消息处理完毕后才安全地退出。在子线程中,如果手动为其创建了Looper,那么在所有的事情完成以后应该调用quit方法来终止消息循环,否则这个子线程就会一直处于等待的状态。
loop()方法:
for (;;) {
    Message msg = queue.next();
// might block
   
if (msg == null ) {
       
// No message indicates that the message queue is quitting.
       
return ;
    }

   
// This must be in a local variable, in case a UI event sets the logger
   
Printer logging = me. mLogging ;
   
if (logging != null ) {
        logging.println(
">>>>> Dispatching to " + msg. target + " " +
                msg.
callback + ": " + msg. what );
    }

    msg.target .dispatchMessage(msg);
...

可以看到,loop()方法也是一个无限循环的方法,它通过消息队列的next方法不断读取队列,前面看到,next()方法是一个没有条件的for循环,如果没有新的消息,会不停阻塞,因此 Message msg = queue.next(); // might block这段代码在没有新的消息进来时会一直阻塞。如果退出消息队列,那么queue.next()方法会返回null,这时就会跳出loop()方法。如果有队列中有新的消息,则会通过 msg.target .dispatchMessage(msg);这一段,将消息分发给发送消息的handler进行处理。而handler的dispatchMessage方法是在创建Handler时所使用的Looper中执行的,这样就成功把代码逻辑切换到指定的线程中去执行了。
来看一下handler中的dispatchMessage 方法:
public void dispatchMessage(Message msg) {
   
if (msg. callback != null ) {
       
handleCallback(msg);
    }
else {
       
if ( mCallback != null ) {
           
if ( mCallback .handleMessage(msg)) {
               
return ;
            }
        }
        handleMessage(msg);
    }
}

private static void handleCallback(Message message) {
    message.
callback .run();
}
msg.callback是一个Runnable对象,其实也就是我们在handler.post(Runnable)中传进去的Runnable对象。可以看到,如果我们调用了handler.post(Runnable)方法,那么接下来就会执行该Runnable对象的run方法。如果我们没有传进去Runnable对象,也就是消息是通过handler.sendMessage等其他方式进行发送的,那么就会进入else代码块,其中的mCallback是Handler内部定义的一个接口:
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
*
@param
msg A {@linkandroid.os.Message Message} object
*
@returnTrue if no further handling is desired
*/
public interface Callback {
   
public boolean handleMessage(Message msg);
}

通过注释介绍,可以看到,通过使用这个接口,我们在初始化handler时无需实现我们自己的handler子类。什么意思呢?一般我们创建handler时,是通过以下的方式进行创建:
private Handler mHandler = new Handler(){
   
@Override
   
public void handleMessage(Message msg) {
       
super .handleMessage(msg);
    }
};

这种方式其实也就是我们创建了一个Handler的子类,我们也可以用如下的方式创建一个handler:
private Handler mHandler = new Handler( new Handler.Callback() {
   
@Override
   
public boolean handleMessage(Message msg) {
       
return false ;
    }
});
这种方式就是上面提到的非创建handler子类的形式。继续回到源码中,如果我们使用了这种方式来创建一个Handler,这时mCallback变量则不为null,此时就会调用该对象的handleMessage方法。如果既没有传入Runnable对象,也不是通过设置Callback对象的方式创建的handler,这时就会执行handler自己的handleMessage方法。
总结一下,handler的创建一般有以下几种方式:
1、直接创建一个handler:
private Handler mHandler = new Handler();
这种方式下,一般搭配Runnable进行消息的处理:
mHandler.post( new Runnable() {
    @Override
   
public void run() {
       
    }
});
通过前面的分析我们知道,这时会直接执行run()方法里面的内容。

2、通过Handler子类的方式创建handler:
private Handler mHandler2 = new Handler(){
   
@Override
   
public void handleMessage(Message msg) {
       
super .handleMessage(msg);
    }
};
此时,可以通过sendMessage来进行消息的发送和处理:
mHandler.sendEmptyMessage( 1 );
这时就会执行handleMessage里面的语句

3、通过传入Callback接口创建handler对象:
private Handler mHandler = new Handler( new Handler.Callback() {
   
@Override
   
public boolean handleMessage(Message msg) {
       
return false ;
    }
});
如果该方法返回false,则只会执行Callback对象的handleMessage方法,如果返回为false,还会执行handler自己的handleMessage方法,这点需要留意。

最后需要说明,handler发送消息到消息队列的方式有多种:
mHandler.sendEmptyMessage( 1 );
mHandler.sendMessage(
new Message());
mHandler.postDelayed(
new Runnable() {
   
@Override
   
public void run() {

    }
},
1000 );
mHandler.post(
new Runnable() {
   
@Override
   
public void run() {
       
    }
});
所有这些方法最终都会到同一个入口方法:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue =
mQueue ;
   
if (queue == null ) {
        RuntimeException e =
new RuntimeException(
               
this + " sendMessageAtTime() called with no mQueue" );
        Log.
w(
"Looper" , e.getMessage(), e);
       
return false ;
    }
   
return enqueueMessage(queue, msg, uptimeMillis);
}
也就是将消息插入到消息队列中,接着就会由looper的loop方法对消息进行处理,接下来的流程前面已经分析过了。是否传入Runnable对象,也会影响到消息处理的流程,这个前面也有了详细说明。至此,整个消息处理流程分析完毕。

-----------------------------------------------------------------------------
提个问题,对于下面的代码,哪个会报错:
1)
public class MyThread extends Thread {
    private Handler mHandler;
    
    public MyThread() {
        mHandler = new Handler();
    }

    @Override
    public void run() {
     mHandler.sendEmptyMessage(1);
    }
}

2)
public class MyThread extends Thread() {
    private Handler mHandler;
    
    @Override 
    public void run() {
     mHandler = new Handler();
     mHandler.sendEmptyMessage(1);
    }
}

在Activity中分别调用上述两个MyThread(),1)不会报错,2)会报错"Can't create handler inside thread that has not called Looper.prepare()"。
调用new Handler()最终会调用这个方法:


1)中的handler是在子线程的构造函数中初始化的,那么这个handler所在的线程其实并不是MyThread这个线程,而是初始化这个MyThread的线程,在这里也就是UI线程。前面提到,UI线程在ActivityThread的main()方法中已经初始化了它的Looper,因此在上图中的mLooper是存在的,可以成功调用new Handler();

而 2)中的handler是在run方法中初始化的,存在于MyThread这个线程中,该线程在调用new Handler()这个方法之前,并没有初始化自己的Looper,因此会报红框中的错误。

对于上面这个问题,需要明白一点,在线程中,只有在run()方法中的代码是运行在这个线程中,其他部分的代码其实是运行在调用这个线程的线程中。可以参考以下代码:





运行结果:

根据结果可以很明显的看到这一结论。这个可能是java很基础的东西,但我确实是今天才意识到这一点。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值