Android(安卓)结合源码和实例理解消息机制
写在前面:
一直对Android消息机制的实现比较感兴趣,尤其是最近在做开发时大量使用这种机制进行网络数据处理后的显示,很有必要进一步总结一下,之前有写过关于消息机制的文章,但是现在看来理解的实在是太肤浅了。在此一并归纳,也算是对之前落下任务的一个交代吧。
首先从一个经典的主线程和子线程进行消息通信的实例入手,一步步进行解析。
主线程和子线程之间消息通信实例
该例子完成了主线程和子线程之间循环发送消息的功能,子线程将一段字符发给主线程,主线程用Toast展示出来,线程之间每隔两秒进行一次消息传递。
主线程类代码如下:
1.package com.elvis.handlerGoActivity;2.import android.app.Activity;3.import android.os.Bundle;4.import android.os.Handler;5.import android.os.Message;6.import android.widget.Toast; 7./** * 这个类是主线程,它每隔两秒就发送一个Message给线程,线程收到Message后9将发送一个Message给主线程,主线程收到Message后会将Message内容Toast出10来 */10.public class HandlerTestActivity extends Activity {11 private Handler uiHandler;12 private ThreadWithLooper thread;13 private Runnable showRunable;14 @Override15 public void onCreate(Bundle savedInstanceState) {16 super.onCreate(savedInstanceState);17 setContentView(R.layout.main);18 uiHandler=new Handler(){19 @Override20 public void handleMessage(Message msg) {21 switch(msg.what){22 case Messages.MSG_HELLO:23 Toast.makeText(HandlerTestActivity.this, (String)msg.obj, Toast.LENGTH_SHORT).show();24 break;25 26 }27 }28 };29 thread=new ThreadWithLooper(uiHandler);30 31 thread.start();32 showRunable=new Runnable() {33 34 @Override35 public void run() {36 //給线程发送一个Message 37thread.getHandler().sendEmptyMessage(Messages.MSG_HELLO);38 uiHandler.postDelayed(this, 2*1000);39 }40 };41 uiHandler.post(showRunable);42 43 }44 45 @Override46 protected void onStop() {47 super.onStop();48 uiHandler.removeCallbacks(showRunable);49 }50}
子线程类代码:
1package com.elvis.handlerGoActivity;2import android.os.Handler;3import android.os.Looper;4import android.os.Message;5 6/** * 从线程发送消息到UI线程(主线程) */9public class ThreadWithLooper extends Thread {10 private Handler handler; 11 private Handler uiHandler; 12 public ThreadWithLooper(Handler mHandler)13 {14 this.uiHandler=mHandler; 15 } 16 public Handler getHandler() {17 return handler;18 } 19 public void setHandler(Handler handler) {20 this.handler = handler;21 } 22 @Override23 public void run() 24 {25 Looper.prepare();26 //初始化Handler,接收到主线程发送过来的Message就回复一个 Message给主线程,消息内容是 一个字符串和当前时间27 handler =new Handler(){28 @Override29 public void handleMessage(Message msg) {30 switch(msg.what){31 case Messages.MSG_HELLO:32 Message message=new Message();33 message.what=Messages.MSG_HELLO;34 message.obj="Yes!I get a hello"+System.currentTimeMillis();35 uiHandler.sendMessage(message);36 break;37 38 }39 } 40 };41 Looper.loop();42 }43 44 45}
公共消息类
用于主线程和子线程之间的消息传递类:Messages
package com.elvis.handlerGoActivity;public class Messages { public static final int MSG_HELLO=0x1;}
程序解析以及源代码剖析
A.—–>
从程序执行的流程上开始分析,首先进入
主线程的29行代码:
29 thread=new ThreadWithLooper(uiHandler);
—->转到子线程的line 12
12 public ThreadWithLooper(Handler mHandler)13 {14 this.uiHandler=mHandler; 15 }
作用是将主线程中的handler赋给子线程中定义的uiHandler,代码很简单,但是要明白:一个Handler只能关联一个线程而且Handler是可以跨线程进行消息发送的,这些消息会被添加到创建该handler的线程所关联的MQ(MessageQueue)上. 另外一个线程要想创建Handler,它必须是Looper线程,即是在线程的Run方法中调用Looper.prepare()和Looper.loop()。每个Activity的主线程默认就是一个Looper线程。
B.—–>
然后走到主线程的31行,
31 thread.start();
功能也很简单,启动线程即是调用thread.run(),这时整个Application已经启动了两个线程。先从子线程的23行开始:
23 public void run() 24 { ..... 定义子线程的handler,同时使子线程成为Looper线程,完成上面需要注意的工作。 定义执行消息处理的方法 }
C.—–> 核心Post()调用
看完子线程的Run方法后,再看下主线程,执行Line 41即是
41 uiHandler.post(showRunable);
千万别小看这个post调用,可谓是一石激起千层浪,要理解Android的消息机制,就必须把这块拎清爽了,那么这个post()函数到底做了些什么呢?下面从源码开始一步一步解开Post()的面纱。
post()源码:
// 此方法用于向关联的MQ上发送Runnable对象,它的run方法将在handler关联的looper线程中执行 public final boolean post(Runnable r) { // 注意getPostMessage(r)将runnable封装成message return sendMessageDelayed(getPostMessage(r), 0); } private final Message getPostMessage(Runnable r) { Message m = Message.obtain(); //得到空的message m.callback = r; //将runnable设为message的callback, return m; } public boolean sendMessageAtTime(Message msg, long uptimeMillis) { boolean sent = false; MessageQueue queue = mQueue; if (queue != null) { msg.target = this; // message的target必须设为该handler! sent = queue.enqueueMessage(msg, uptimeMillis); } else { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); } return sent; }
由上面的源码可以知道,post()函数将一个Runnable对象组装为一个Message对象,并将该Message.callBacks成员赋值为 Runnable的对象,最后将消息添加到Handler相关联线程的消息队列上(MessageQueue)。
D——–>
消息是由uiHandler发送且uiHandler是在主线程中定义的,因此post()调用后组装的Message会被添加到主线程的消息队里中,这里主线程默认是Looper线程,它会一直调用Looper.loop()方法,好的,到这里有必要深入研究一下loop()的工作内容。
Loop()源码
public static final void loop() { Looper me = myLooper(); //得到当前线程Looper MessageQueue queue = me.mQueue; //得到当前looper的MQ // 这两行没看懂= = 不过不影响理解 Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); // 开始循环 while (true) { Message msg = queue.next(); // 取出message if (msg != null) { if (msg.target == null) { // message没有target为结束信号,退出循环 return; } // 日志。。。 if (me.mLogging!= null) me.mLogging.println( ">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what ); // 非常重要!将真正的处理工作交给message的target,即后面要讲的handler msg.target.dispatchMessage(msg); // 还是日志。。。 if (me.mLogging!= null) me.mLogging.println( "<<<<< Finished to " + msg.target + " " + msg.callback); // 下面没看懂,同样不影响理解 final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf("Looper", "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } // 回收message资源 msg.recycle(); } } }
Loop()函数完成的功能很单调:它不断从自己的MQ中取出队头的消息(也叫任务)执行,如图示:
下面的消息分派代码很重要:
msg.target.dispatchMessage(msg);
E.——–>
程序走到这里的时候,就有必要深入学习一下 despatchMessage(Message msg)d的工作内容,贴上源码一起看看:
// 处理消息,该方法由looper调用 public void dispatchMessage(Message msg) { if (msg.callback != null) { // 如果message设置了callback,即runnable消息,处理callback! handleCallback(msg); } else { // 如果handler本身设置了callback,则执行callback if (mCallback != null) { /* 这种方法允许让activity等来实现Handler.Callback接口,避免了自己编写handler重写handleMessage方法。见http://alex-yang-xiansoftware-com.iteye.com/blog/850865 */ if (mCallback.handleMessage(msg)) { return; } } // 如果message没有callback,则调用handler的钩子方法handleMessage handleMessage(msg); } } // 处理runnable消息 private final void handleCallback(Message message) { message.callback.run(); //直接调用run方法! } // 由子类实现的钩子方法 public void handleMessage(Message msg) { }
分析源代码可知,handler对消息的处理是通过dispatchMessage()和 之类所实现的“可以称为回调函数”的handleMessage()共同完成的。
理论结合实际—实战
好的,代码看到这里,就好比是我们游泳时,一头扎进了水里,是不是有点晕,缺氧了!没关系我们浮上来透口气…….上面写的这么多,只不过是uiHandler调用post()后application默默完成的工作。
那现在我们就理论联系实际进一步剖析uiHandler.post(showRunable)执行后,application完成的工作。
1.post()之后,showRunale对象被封装为一个Message对象m,注意:m.callbacks = showRunnable;该消息被添加到主线程的消息队列上。
2.主线程默默执行loop(),将该消息发给消息处理函数dispatchMessage(),在这个函数中,会执行下面的代码:
if (msg.callback != null) { // 如果message设置了callback,即runnable消息,处理callback! handleCallback(msg); }
然后会执行
// 处理runnable消息 private final void handleCallback(Message message) { message.callback.run(); //直接调用run方法! }
这个地方很重要,post()传入一个Runnable对象后,它没有调用handleMessage(),而是直接调用Runnable对象的Run方法。
3.转到主线程showRunable对象中run方法的定义,如下:
showRunable=new Runnable() {33 34 @Override35 public void run() {36 //給线程发送一个Message 37thread.getHandler().sendEmptyMessage(Messages.MSG_HELLO);38 uiHandler.postDelayed(this, 2*1000);39 }40 };
Line 37行:向子线程中定义的Handler发送一个Message.what =MSG_HELLO的消息,该消息会被添加到子线程的消息队列中,然后是子线程的loop()调用,这个地方一定要注意:该消息的msg.callback = null.因此在执行dispatchMessage()时,走的是下面的代码:
// 如果message没有callback,则调用handler的钩子方法handleMessage handleMessage(msg);
4.看下子线程中handler的handleMessage()方法:
//初始化Handler,接收到主线程发送过来的Message就回复一个 Message给主线程,消息内容是 一个字符串和当前时间27 handler =new Handler(){28 @Override29 public void handleMessage(Message msg) {30 switch(msg.what){31 case Messages.MSG_HELLO:32 Message message=new Message();33 message.what=Messages.MSG_HELLO;34 message.obj="Yes!I get a hello"+System.currentTimeMillis();35 uiHandler.sendMessage(message);36 break;37 38 }39 } 40 };
注释已经解释的很清楚了。在这里要补充一下:子线程中的uiHandler处理的消息队列是主线程的消息队列。因为传入的mHandler是主线程中定义的handler,关联的是主线程的MQ。
这点可以用下图进行描述:
public ThreadWithLooper(Handler mHandler)13 {14 this.uiHandler=mHandler; 15 }
5.由此我们再一次把视线转移到主线程中,因为主线程的消息队列接收到了一条消息,同样这条消息的msg.callback = null, 这样在主线程的loop()执行后,会调用handleMessage()方法,好的,一起来看下主线程的消息处理函数定义:
uiHandler=new Handler(){19 @Override20 public void handleMessage(Message msg) {21 switch(msg.what){22 case Messages.MSG_HELLO:23 Toast.makeText(HandlerTestActivity.this, (String)msg.obj, Toast.LENGTH_SHORT).show();24 break;25 26 }27 }28 };
代码很简单,只是在接收到消息后,Toast一下。
到此我们已经完整了走完了一遍下面的流程:
主线程发送消息给子线程- - - - - - - 子线程在消息处理时向主线程发送消息- - - - - -主线程接收到消息后展示消息内容。
到此是不是已经结束了呢? 呵 呵,别急,我们的代码才跑到这里,
37 thread.getHandler().sendEmptyMessage(Messages.MSG_HELLO);
下面的这句话才是完成主线程和子线程之间消息循环发送的关键。
6.
38 uiHandler.postDelayed(this, 2*1000);
postDelayed()函数跟post()函数执行的功能和过程基本上是一致的,只是在执行
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
uptimeMillis = 2*1000,即是延迟两秒发送消息,前面已经阐述过。post会调用runnable的Run()方法,如此会循环调用自己,实现类似于定时器(间隔两秒)的消息发送,至此整个主线程和子线程之间的消息循环发送就启动起来。
7.消息循环发送建立起来之后,会一直执行下去,在程序退出时一定要终止整个循环,下面的这段代码就是为了完成这个功能:
@Override46 protected void onStop() {47 super.onStop();48 uiHandler.removeCallbacks(showRunable);49 }
removeCallbacks(showRunnable),从名字上可以看出,就是解除消息队列上所有消息绑定的Runnable对象。
即是将主线程上消息的:msg.callback= null;
再次转到主线程的dispatchMessage()的时候,就不会调用Runnable的Run方法,转而执行uiHandler的handleMessage(),至此完全终止消息循环。
总结和感言:
第一次写这么长的博客,虽说不易,但是在总结的过程中收获颇多,很多写之前模糊和没有考虑到的地方在写作的过程中逐渐清晰和明朗,颇有一种拨云雾而见青天的感觉。这篇文章是站在巨人的肩膀上然后自己理清思路总结归纳的,中间参考了小巫的消息机制,在此要感谢一下小巫,也同样感谢网络上那些主动分享的大神们,向你们致敬!!!
更多相关文章
- Android(安卓)的进程与线程总结
- Android中利用Handler在子线程中更新界面--简单的小球上下跳动案
- Android主线程looper是死循环问题
- android的Thread、Runnable、Asyntask的区别与联系
- Study on Android【六】--消息机制,异步和多线程
- [置顶] 我的Android进阶之旅------>Android基于HTTP协议的多线程
- 实现Android监控任意控件或按键双击事件方法
- Android中后台定时任务实现,即时数据同步问题思考!
- android面试经典(6)