我写这篇日志的初衷,是想让每个初学Android应用开发的同学们(高手就飘过吧~~)弄明白Android消息机制的原理和底层实现方式,至于大家能否对Android消息机制彻底弄明白,纯粹是对我个人表达能力的一次巨大挑战。因为Android事件机制和代码实现,对已经具备Java或C++语言语法基础的人来讲,其实是并不难理解的。在我看来,消息机制有2大应用场合,一是GUI程序设计,比如我们用鼠标在一个程序界面上实施鼠标点击,拖拽,键盘输入等动作,程序一般便会执行预期的效果。二是后台服务程序,没有程序界面的那种。比如说一些银行金融产品软件,电信设备的操作维护平台软件等。我们一次网上银行汇款,一次拨打电话行为,都会激发大量的系统内部消息和消息处理动作,这类系统往往还会添加分布式组件模块,屏蔽消息跨平台的发送和接收机制,但其核心设计思想仍基于GUI程序的消息处理模型。既然如此,我们从GUI程序说起吧。在界面上,一个看似简单的操作背后,其实隐藏了GUI程序设计核心的三要素:线程,消息队列和消息响应函数。当然了,还有些具体的细节过程,比如如何发送消息,取得消息,执行消息响应函数,消息循环的停止等。为便于更好的说明这三要素,我把线程,消息队列和消息响应函数,变通成一个邮局的工作流,可以把邮递员看成是线程,邮件看成是消息,那么邮筒就是一个消息队列了,邮递员把邮件投递到邮件接收者的过程看成消息的响应函数。邮局的工作流程大概如下图:简单说明:头天路人甲,乙,丙分别向邮局的邮筒里投递了3封邮件:mail 1,mail 2,mail 3,拼命三郎今早上班时和往常一样从邮筒中取邮件时发现了这3封邮件,然后骑上自备的电炉乐呵乐呵的按照mail 1,mail 2,mail3上的地址送到接收人手里,然后再返回邮件取新的邮件,运气不错,拼命三郎没取到新的邮件,可以好好歇歇了^_^。但我们还不能歇息,继续探究拼命三郎取送邮件的过程,和Android消息机制的关联性,探究完了我们也就明白Android消息机制的来龙去脉了。下面掌声有请Android消息机制的三大部件隆重登场(鼓掌~~~)Thread:大家好,我是线程,关于我的身世我在这里没什么好讲的,有兴趣的同学可以参阅操作系统的原理方面的书籍和资料,里面会有我前世今生的详细介绍。平常,大家没少跟我打交道。诸位最愿意干的事情就是在我的Run方法里写一个带while循环的函数,让我不停地在这个函数里面兜圈。老实讲,虽然这样让我很累,和感觉些许的无聊,但这是我的职责所在——毫无怨言的执行Run方法的代码指令,我的这个特点和上图中的整天不停取,送邮件的拼命三郎很像吧。Looper:各位同学好,我的名字大伙都认识的,大家猜猜我是什么循环者?xx: 等等,这个听上去很熟啊,Thread不是经常在Run方法里做循环的嘛?!你能说讲你和Thread是什么关系?Looper继续道:xx同学将来一定是块写程序的料,思维很有想象力嘛。在我内部,确实有一个叫loop的消息循环函数和一个消息队列。我再透露一点,在我的一个实例产生时,它就被上天安排和一个Thread男绑定在一起的时候,可以说是真正意义上的执子之手白头到老了。从那刻起,我便拥有了Thread一生,Thread对我也是不离不弃。为什么?一个线程永远只能拥有一个Looper对象,同时Thread在run方法中会进入我的loop消息循环。不管白天和黑夜,Thread做的事情只有一件:永不停歇地从我的消息队列里获取消息,然后执行消息的target定义的行为——这就是传说中的消息循环,直到程序主动退出循环。执行消息的target定义的行为,这点和拼命三郎,根据邮件的收信人的地址送信一样啊,不同的接收人地址,决定了送邮件的路径和路程是不一样的。同样的,不同的消息其处理方式也是不一样的。Handler:大家辛苦了,久等了,我接着刚才Looper说到的—消息的target说起,消息的target实际的扮演者就是我啦。从我的名字也显现了我的身份——处理者,即消息响应处理者。还有一点:大家知道Looper中消息队列中是怎么来的吗?嗯,对,答案是通过我投递的。那我是怎么拿到Looper的消息队列的呢?其实,在new一个我的实例的时候,会有个Looper实例也跟我绑定一下,这样通过我发送的消息就自动添加到对应Looper的消息队列了,通过上面Looper的自我介绍,也就决定了这条消息是发个那个线程的,是吧^_^。通过上面三大部件的一段真情对白,相信大家对Android事件机制已经有了清晰的感性认识吧。下面我们再从代码实现的角度来剖析:首先,看看一个Looper对象如何跟一个Thread对象绑定的,这里我拿HandlerThread作为解剖标本,它的类是Android SDK中自带Looper的线程子类,重点就在其run方法中:public class HandlerThread extends Thread { private int mPriority; private int mTid = -1; private Looper mLooper; public HandlerThread(String name) { super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; } public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1;}}1.run方法中第一行获取线程所在进程的id。2.第2行有点陌生,其效果就是使当前线程绑定一个Looper对象啦。按F3跟踪展开Looper.prepare()函数如下: public static final void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper());}在线程的本地存储区域中设置了一个Looper对象实例。在设置之前作一次检测,如果当前线程已经绑定一个Looper实例,程序则抛出异常。这就是前面为什么说到的Thread对一个Looper不离不弃,生死相依的原因。按F3继续深入,看看在Looper对象的构造中能否挖掘到消息队列影子~~ private Looper() { mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread();}Okay,在Looper的私有构造函数中第1行,一个消息队列MessageQueue终于诞生了。至于MessageQueue长成什么样子,大家可以再继续按F3跟踪,这里它以不是重点。3.我们展开第4行mLooper = Looper.myLooper();源码如下 public static final Looper myLooper() { return (Looper)sThreadLocal.get();}把当前线程绑定的Looper对象赋值给mLooper。4.第8行onLooperPrepared();是在进入消息循环之前留给用户自定义的场合了,在HandlerThread类中其实现为空。5.关键的第9行Looper.loop();相信大家都能猜到这行干的事情了——消息循环。还等什么,赶紧跟进去一趟究竟吧~~~ public static final void loop() { Looper me = myLooper(); // 得到当前线程的绑定的Looper对象 MessageQueue queue = me.mQueue; // 从Looper对象上获取消息队列对象 while (true) { // 呵呵,折腾线程的while循环 Message msg = queue.next(); // might block 从消息队列中获取到一条消息,队列为空时阻塞 //if (!me.mRun) { // break; //} if (msg != null) { if (msg.target == null) { // No target is a magic identifier for the quit message. return; // 如果消息的target为空,while循环退出哦,这也是终止消息循环的方法 } if (me.mLogging!= null) me.mLogging.println( ">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what ); msg.target.dispatchMessage(msg); // msg.target就是Handler对象,所以消息的响应函数就是Handler的dispatchMessage方法,在dispatchMessage方法中又会调用到handleMessage()方法,所以我们只须定义handleMessage()就能实现对消息的处理。 if (me.mLogging!= null) me.mLogging.println( "<<<<< Finished to " + msg.target + " " + msg.callback); msg.recycle(); } } }Okay,通过上面代码的剖析,我们看到了HandlerThread类中run方法如何创建Looper对象,MessageQueue对象,线程进入消息循环的过程,以及消息循环里面具体干啥子事儿。现在离大功告成还差最后一步了,那就是消息是如何投递到消息队列中去的?前面说到是由Handler对象投递的,那么Handler应该先拿到Looper的消息队列,然后通过消息队列的某一成员方法将消息添加到消息队列中即可。事实是否如我们推测的这样呢,老方法跟踪源码。第一步,Handler必须要拿到一个消息队列的引用。看看Handler的Handler(Looper looper)构造函数,很明显,Handler的mQueue对象拿到了指定looper对象的mQueue。 public Handler(Looper looper) { mLooper = looper; mQueue = looper.mQueue; mCallback = null; }Handler的构造函数还有另一个版本——没有指定Looper,这里则以当前线程绑定的Looper对象的消息队列赋值给Handler的mQueue。 public Handler() { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = null; }第二步,通过Handler投递消息。 public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0);}继续跟踪: public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }继续跟踪: public boolean sendMessageAtTime(Message msg, long uptimeMillis) { boolean sent = false; MessageQueue queue = mQueue; if (queue != null) { msg.target = this; sent = queue.enqueueMessage(msg, uptimeMillis);// 这里通过queue的enqueueMessage方法把msg加入到消息队列中去了 } else { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); } return sent; }好吧,如果诸位有耐心看到这里,应该对Android的消息机制有新的认识和自己的看法了。再结合示例代码调试稍加练习,一定会有所收获。如果问题欢迎拍砖交流O(∩_∩)O

更多相关文章

  1. 浅析Android中的消息机制-解决:Only the original thread that cr
  2. Android异步消息机制之Handler
  3. Android的Handler机制详解3_Looper.looper()不会卡死主线程
  4. Android之Handler用法总结
  5. Android开发之消息处理机制(一)——Handler
  6. Android异步加载图像小结 (含线程池,缓存方法)
  7. android 面试题集
  8. Titanium 使用刘明星的Jpush module做android端的消息推送
  9. android 电容屏(二):驱动调试之基本概念篇

随机推荐

  1. Java、Android中Math详解
  2. Android(安卓)apk应用程序签名
  3. Android(安卓)6种快速开发框架
  4. Android(安卓)Camera Hal 的初步实现1
  5. php脚本生成google play url的下载链接,下
  6. Android之Handle的使用[二]
  7. Android(安卓)中Uri的用法汇总
  8. Android(安卓)ril原生代码(C/C++)和java代码部
  9. Android横竖屏切换正确实现方式
  10. Android中TextView的某一关键字高亮显示