深入剖析Android消息机制

在android 中,线程内部或者线程之间进行信息交互时经常会使用消息,这些基础的东西如果我们熟悉其内部的原理,将会使我们容易、更好地架构系统,避免一些低级的错误。在学习android中消息机制之前,我们先了解与消息有关的几个类:
1.message
消息对象,顾名思义就是记录消息信息的类。这个类有几个比较重要的字段:
a.arg1和arg2:我们可以使用两个字段用来存放我们需要传递的整型值,在service中,我们可以用来存放service的id。
b.obj:该字段是object类型,我们可以让该字段传递某个多项到消息的接受者中。
c.what:这个字段可以说是消息的标志,在消息处理中,我们可以根据这个字段的不同的值进行不同的处理,类似于我们在处理button事件时,通过switch(v.getid())判断是点击了哪个按钮。
在使用message时,我们可以通过new message()创建一个message实例,但是android 更 推荐我们通过message.obtain()或者handler.obtainmessage()获取message对象。这并不一定是直接创建一个新 的实例,而是先从消息池中看有没有可用的message实例,存在则直接取出并返回这个实例。反之如果消息池中没有可用的message实例,则根据给定 的参数new一个新message对象。通过分析源码可得知,android系统默认情况下在消息池中实例化10个message对象。
2.messagequeue
消息队列,用来存放message对象的数据结构,按照“先进先出”的原则存放消息。存放并非实际意义的保存,而是将message对象以链表的方 式串联起来的。messagequeue对象不需要我们自己创建,而是有looper对象对其进行管理,一个线程最多只可以拥有一个 messagequeue。我们可以通过looper.myqueue()获取当前线程中的messagequeue。
3.looper
messagequeue的管理者,在一个线程中,如果存在looper对象,则必定存在messagequeue对象,并且只存在一个looper对象和一个messagequeue对象。在android 系统中,除了主线程有默认的looper对象,其它线程默认是没有looper对象。如果想让我们新创建的线程拥有looper对象时,我们首先应调用looper.prepare()方法,然后再调用looper.loop()方法。典型的用法如下:
view plaincopy to clipboardprint?
class looperthread extends thread
{
public handler mhandler;
public void run()
{
looper.prepare();
//其它需要处理的操作
looper.loop();
}
}
倘若我们的线程中存在looper对象,则我们可以通过looper.mylooper()获取,此外我们还可以通过 looper.getmainlooper()获取当前应用系统中主线程的looper对象。在这个地方有一点需要注意,假如looper对象位于应用程 序主线程中,则looper.mylooper()和looper.getmainlooper()获取的是同一个对象。
4.handler
消息的处理者。通过handler对象我们可以封装message对象,然后通过sendmessage(msg)把message对象添加到 messagequeue中;当messagequeue循环到该message时,就会调用该message对象对应的handler对象的 handlemessage()方法对其进行处理。由于是在handlemessage()方法中处理消息,因此我们应该编写一个类继承自 handler,然后在handlemessage()处理我们需要的操作。
view plaincopy to clipboardprint?
# public class messageservice extends service
#
# {
# private static final string tag = “messageservice”;
# private static final int kuka = 0;
# private looper looper;
# private servicehandler handler;
# /**
# * 由于处理消息是在handler的handlemessage()方法中,因此我们需要自己编写类
# * 继承自handler类,然后在handlemessage()中编写我们所需要的功能代码
# * @author coolszy
# *
# */
# private final class servicehandler extends handler
# {
# public servicehandler(looper looper)
# {
# super(looper);
# }
#
# @override
# public void handlemessage(message msg)
# {
# // 根据what字段判断是哪个消息
# switch (msg.what)
# {
# case kuka:
# //获取msg的obj字段。我们可在此编写我们所需要的功能代码
# log.i(tag, “the obj field of msg:” + msg.obj);
# break;
# // other cases
# default:
# break;
# }
# // 如果我们service已完成任务,则停止service
# stopself(msg.arg1);
# }
# }
#
# @override
# public void oncreate()
# {
# log.i(tag, “messageservice-->oncreate()”);
# // 默认情况下service是运行在主线程中,而服务一般又十分耗费时间,如果
# // 放在主线程中,将会影响程序与用户的交互,因此把service
# // 放在一个单独的线程中执行
# handlerthread thread = new handlerthread(“messagedemothread”, process.thread_priority_background);
# thread.start();
# // 获取当前线程中的looper对象
# looper = thread.getlooper();
# //创建handler对象,把looper传递过来使得handler、
# //looper和messagequeue三者建立联系
# handler = new servicehandler(looper);
# }
#
# @override
# public int onstartcommand(intent intent, int flags, int startid)
# {
# log.i(tag, “messageservice-->onstartcommand()”);
#
# //从消息池中获取一个message实例
# message msg = handler.obtainmessage();
# // arg1保存线程的id,在handlemessage()方法中
# // 我们可以通过stopself(startid)方法,停止服务
# msg.arg1 = startid;
# // msg的标志
# msg.what = kuka;
# // 在这里我创建一个date对象,赋值给obj字段
# // 在实际中我们可以通过obj传递我们需要处理的对象
# date date = new date();
# msg.obj = date;
# // 把msg添加到messagequeue中
# handler.sendmessage(msg);
# return start_sticky;
# }
#
# @override
# public void ondestroy()
# {
# log.i(tag, “messageservice-->ondestroy()”);
# }
#
# @override
# public ibinder onbind(intent intent)
# {
# return null;
# }
# }
注:在测试代码中我们使用了handlerthread类,该类是thread的子类,该类运行时将会创建looper对象,使用该类省去了我们自己编写thread子类并且创建looper的麻烦。
下面我们通过查看源码,分析下程序的运行过程:
1.oncreate()
首先启动服务时将会调用oncreate()方法,在该方法中我们new了一个handlerthread对象,提供了线程的名字和优先级。
紧接着我们调用了start()方法,执行该方法将会调用handlerthread对象的run()方法:
view plaincopy to clipboardprint?
public void run() {
mtid = process.mytid();
looper.prepare();
synchronized (this) {
mlooper = looper.mylooper();
notifyall();
}
process.setthreadpriority(mpriority);
onlooperprepared();
looper.loop();
mtid = -1;
}
在run()方法中,系统给线程添加的looper,同时调用了looper的loop()方法:
view plaincopy to clipboardprint?
1. public static final void loop() {
2.
3. looper me = mylooper();
4. messagequeue queue = me.mqueue;
5. while (true) {
6. message msg = queue.next(); // might block
7. //if (!me.mrun) {
8. // break;
9. //}
10. if (msg != null) {
11. if (msg.target == null) {
12. // no target is a magic identifier for the quit message.
13. return;
14. }
15. if (me.mlogging!= null) me.mlogging.println(
16. “>>>>> dispatching to ” + msg.target + “ ”
17. + msg.callback + “: ” + msg.what
18. );
19. msg.target.dispatchmessage(msg);
20. if (me.mlogging!= null) me.mlogging.println(
21. “<<<<< finished to ” + msg.target + “ ”
22. + msg.callback);
23. msg.recycle();
24. }
25. }
26. }
通过源码我们可以看到loop()方法是个死循环,将会不停的从messagequeue对象中获取message对象,如果messagequeue 对象中不存在message对象,则结束本次循环,然后继续循环;如果存在message对象,则执行 msg.target.dispatchmessage(msg),但是这个msg的.target字段的值是什么呢?我们先暂时停止跟踪源码,返回到 oncreate()方法中。线程执行完start()方法后,我们可以获取线程的looper对象,然后new一个servicehandler对象, 我们把looper对象传到servicehandler构造函数中将使handler、looper和messagequeue三者建立联系。
2.onstartcommand()
执行完onstart()方法后,将执行onstartcommand()方法。首先我们从消息池中获取一个message实例,然后给 message对象的arg1、what、obj三个字段赋值。紧接着调用sendmessage(msg)方法,我们跟踪源代码,该方法将会调用 sendmessagedelayed(msg, 0)方法,而sendmessagedelayed()方法又会调用sendmessageattime(msg, systemclock.uptimemillis() + delaymillis)方法,在该方法中我们要注意该句代码msg.target = this,msg的target指向了this,而this就是servicehandler对象,因此msg的target字段指向了 servicehandler对象,同时该方法又调用messagequeue 的enqueuemessage(msg, uptimemillis)方法:
view plaincopy to clipboardprint?
# final boolean enqueuemessage(message msg, long when) {
# if (msg.when != 0) {
# throw new androidruntimeexception(msg
# + “ this message is already in use.”);
# }
# if (msg.target == null && !mquitallowed) {
# throw new runtimeexception(“main thread not allowed to quit”);
# }
# synchronized (this) {
# if (mquiting) {
# runtimeexception e = new runtimeexception(
# msg.target + “ sending message to a handler on a dead thread”);
# log.w(“messagequeue”, e.getmessage(), e);
# return false;
# } else if (msg.target == null) {
# mquiting = true;
# }
# msg.when = when;
# //log.d(“messagequeue”, “enqueing: ” + msg);
# message p = mmessages;
# if (p == null || when == 0 || when handler.sendmessageattime()-->msg.target = this;queue.enqueuemessage==>把msg添加到消息队列中
3.handlemessage(msg)
onstartcommand()执行完毕后我们的service中的方法就执行完毕了,那么handlemessage()是怎么调用的呢?在前 面分析的loop()方法中,我们当时不知道msg的target字段代码什么,通过上面分析现在我们知道它代表servicehandler对 象,msg.target.dispatchmessage(msg);则表示执行servicehandler对象中的 dispatchmessage()方法:
view plaincopy to clipboardprint?
1. public void dispatchmessage(message msg) {
2. if (msg.callback != null) {
3. handlecallback(msg);
4. } else {
5. if (mcallback != null) {
6. if (mcallback.handlemessage(msg)) {
7. return;
8. }
9. }
10. handlemessage(msg);
11. }
12. }
该方法首先判断callback是否为空,我们跟踪的过程中未见给其赋值,因此callback字段为空,所以最终将会执行handlemessage()方法,也就是我们servicehandler类中复写的方法。在该方法将根据what字段的值判断执行哪段代码。
至此,我们看到,一个message经由handler的发送,messagequeue的入队,looper的抽取,又再一次地回到handler的怀抱中。而绕的这一圈,也正好帮助我们将同步操作变成了异步操作。

音响产品的CCC认证知多少
解析数据中心选择思科Nexus 9000系列交换机的原因及互连方案
最先进昂贵的网络安全工具就是最可靠的吗
什么是LPO?LPO和传统光模块的关键区别
车联网系统_车联网系统的组成_车联网系统架构图
深入剖析Android消息机制
我国电力电缆市场尚未形成行业龙头,高端电力电缆国产化率有待提升
分析吉利 EV450 高压互锁故障原因
M12连接器4芯8芯传感器连接线接近开关插头
物联网网关的主要功能是什么?
Vishay在2013中国电子展成都站将展出其最新的业界领先技术
iOS10.3正式版上线,这些新功能让开发者们兴奋!
物联网环境监测需要注意什么问题
目前工业机器人的发展现状是怎样的
中国智能手机如何突破重围高速增长?
白光轮廓仪与共聚焦显微镜的区别是什么?
基于PoE的LED照明系统的解决方案
新能源汽车下乡,设备制造商如何提供更高效智能的运维服务
骁龙450和636的区别
利用Block Design加速设计