Android:Handler的消息机制
前言
Android
的消息机制原理是Android
进阶必学知识点之一,在Android
面试也是常问问题之一。在Android
中,子线程是不能直接操作View
,需要切换到主线程进行。那么这个切换动作就涉及到了Android
的消息机制,也就是本文要讲的Handler、Looper、MessageQueue、Message它们之间的关系。
Handler
Handler
在消息机制中扮演发送消息和处理消息的角色,也是我们平常接触最多的类。
Handler如何处理消息?
下面代码展示Handler
如何处理消息。新建Handler
对象,并重写handleMessage,在方法内处理相关逻辑,一般处理和主线程相关的逻辑。Handler有很多的构造器,下面构造器常用在主线程。
private Handler handler=new Handler(){@Overridepublic void handleMessage(Message msg) {if (msg.what==1){Toast.makeText(MainActivity.this,"handle message",Toast.LENGTH_LONG).show();}}};复制代码
Handler是如何发送消息的呢?
通过下面的代码可以了解到,Handler
对象支持发送Message
和Runable
。Runable最终被包装成Message
的callback
实例变量(Handler
对象处理消息会优先处理callback
的逻辑),和Message
一样的方式放到消息队列中。而每个方法都有相关的变形,支持延迟发送,或者未来的某段时间里发送等等。
//在消息池获取消息体,能达到消息重用,如果消息池没有消息,则新建消息Message msg = handler.obtainMessage();msg.what = 1;//发送消息handler.sendMessage(msg);//发送空消息,参数会自动被包装msg.what=1handler.sendEmptyMessage(1);//未来的时间里发送消息handler.sendEmptyMessageAtTime(1, 1000);//延迟发送消息handler.sendEmptyMessageDelayed(1, 1000);msg = handler.obtainMessage();msg.what = 2;//将消息发送消息队列前面handler.sendMessageAtFrontOfQueue(msg);//发送任务,run方法内容将handler被处理。handler.post(new Runnable() {@Overridepublic void run() {Log.i("Handler", "Runnable");}});
复制代码
如果平常使用,我们只需要主线程定义Hanlder
处理消息的内容,在子线程发送消息即可达到切换流程。
Looper
Looper
负责循环的从消息队列中取消息,发送给Handler
处理。因为消息队列只用来存储消息,所以需要Looper
不断的从消息队列中取消息给Handler
。默认情况,所有线程并不拥有Looper
。如果在子线程直接执行Looper.loop
方法,就会发生异常。那主线程为什么不会报错?在App的启动流程中,创建ActivityThread
时,会调用Looper.prepare
来创建Looper
和MessageQueue
,和Looper.loop
开启循环。也就是系统为我们在主线程创建Looper
和MessageQueue
。所以,在子线创建Handler
前,需要先调用Looper.prepare
方法,创建Looper
和MessageQueue
。IntentService
就是这样实现的。点击看IntentService的知识点。
MessageQueue
MessageQueue内部是以链表的形式组织的,主要作用是存储Message
。在创建Looper
的时候,会自动创建MessageQueue
。
三者关系形成了Android的消息机制
Handler
发送消息时会将消息插入到MessageQueue
,而Looper
不断的从MessageQueue
中取消息分发给Handler
处理。
源码解析
Handler的构建
我们先看一下Handler的构造器代码。
public Handler(Callback callback, boolean async) {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 " + Thread.currentThread()+ " that has not called Looper.prepare()");}//分析二mQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async;}复制代码
Handler
有很多重载的构造器,我们常用在使用默认构造器,最终会调用上面的构造器。
分析一
通过Looper.myLooper()
,获Looper
的实例。而在myLooper
的实现中,是通过ThreadLocal
的get
方法来获取的。如果ThreadLocal
不存在Looper
,则放回null
。ThreaLocal
这里可以简单理解为保存当前线程私有独立的实例,其他线程不可访问。如果ThreadLocal
不存在Looper
实例则,返回null
。这也就是前面说的,在子线程创建Handler
前,需要先调用Looper.prepare
方法。否则会抛出RuntimeException
。
public static @Nullable Looper myLooper() {return sThreadLocal.get();}
复制代码
分析二
mQueue
为Looper
中的消息队列,mCallBack
定义了一个接口,用于回调消息处理。
public interface Callback {/*** @param msg A {@link android.os.Message Message} object* @return True if no further handling is desired*/public boolean handleMessage(Message msg);}
复制代码
Handler发送消息
Handler所有发送消息方法的变体最终都会以下面方法放去到消息队列中。
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);}private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}复制代码
这里最重要的就是enqueueMessage
方法中,将当前Handler
对象设置给Message
的target
变量。然后调用队列queue
的enqueueMessage
方法。
boolean enqueueMessage(Message msg, long when) {if (msg.target == null) {throw new IllegalArgumentException("Message must have a target.");}if (msg.isInUse()) {throw new IllegalStateException(msg + " This message is already in use.");}synchronized (this) {if (mQuitting) {IllegalStateException e = new IllegalStateException(msg.target + " sending message to a Handler on a dead thread");Log.w(TAG, e.getMessage(), e);msg.recycle();return false;}msg.markInUse();msg.when = when;Message p = mMessages;boolean needWake;//如果队列为空或者插入message未来处理时间小于当前对头when//则将当前消息设为队列头if (p == null || when == 0 || when < p.when) {// New head, wake up the event queue if blocked.msg.next = p;mMessages = msg;needWake = mBlocked;} else {needWake = mBlocked && p.target == null && msg.isAsynchronous();Message prev;for (;;) {prev = p;p = p.next;if (p == null || when < p.when) {break;}if (needWake && p.isAsynchronous()) {needWake = false;}}msg.next = p; // invariant: p == prev.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;}复制代码
而在MessageQueue
的enqueueMessage
方法中,会先检查target
是否null
,message
是否应在使用,当前线程是否退出,死亡状态。如果是,则抛出异常。如果当前队列是空或者阻塞,直接当前Message
对象设为队列的头并唤醒线程。如果不是,则根据Message
对象的when
插入到队列合适的位置。因此可以看得出,Handler
发送消息时是将消息放到队列中。
Looper和MessageQueue的创建
前面讲过,子线程使用Handler,需要调用Looper
的静态prepare
方法。
public static void prepare() {prepare(true);}private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));}
复制代码
如果当前线程已经有Looper
,代用Looper就会报错。如果没有,new Looper
并保存到ThreadLocal
中。new Looper
非常简单,只是新建一个MessageQueue
,和持有当前线程。
private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}
复制代码
Looper是如何实现循环的
在调用了Looper.prepare
创建Looper
和MessageQueue
对象后,要调用Loop.loop
的开始循环分发消息队列中消息。
public static void loop() {final Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}final MessageQueue queue = me.mQueue;// Make sure the identity of this thread is that of the local process,// and keep track of what that identity token actually is.Binder.clearCallingIdentity();final long ident = Binder.clearCallingIdentity();// Allow overriding a threshold with a system prop. e.g.// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'final int thresholdOverride =SystemProperties.getInt("log.looper."+ Process.myUid() + "."+ Thread.currentThread().getName()+ ".slow", 0);boolean slowDeliveryDetected = false;//分析一for (;;) {Message msg = queue.next(); // might blockif (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 loggerfinal Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}final long traceTag = me.mTraceTag;long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;if (thresholdOverride > 0) {slowDispatchThresholdMs = thresholdOverride;slowDeliveryThresholdMs = thresholdOverride;}final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);final boolean needStartTime = logSlowDelivery || logSlowDispatch;final boolean needEndTime = logSlowDispatch;if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {Trace.traceBegin(traceTag, msg.target.getTraceName(msg));}final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;final long dispatchEnd;try {//分析二:msg.target.dispatchMessage(msg);dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}if (logSlowDelivery) {if (slowDeliveryDetected) {if ((dispatchStart - msg.when) <= 10) {Slog.w(TAG, "Drained");slowDeliveryDetected = false;}} else {if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",msg)) {// Once we write a slow delivery log, suppress until the queue drains.slowDeliveryDetected = true;}}}if (logSlowDispatch) {showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);}if (logging != null) {logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);}final long newIdent = Binder.clearCallingIdentity();if (ident != newIdent) {Log.wtf(TAG, "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);}msg.recycleUnchecked();}}复制代码
分析一: 通过无限制的for
循环,读取队列的消息。而MessageQueue
的next
方法内部通过链表的形式,根据when
属性的顺序返回message
。
分析二: 调用Message
对象的target
的dipatchMessage
方法。这里的target
就是发送消息的Handler
对象。而在Handler
对象的dipatchMessage
方法中,优先执行Message
对象的callback
方法,即优先执行我们发送消息时以Runable
发送的任务,如果有的话。不然检测Callback
对象的handleMessage
方法,最后才是我们重写Hanlder
对象的handleMessage
方法。因为Handler
不仅有默认构造函数,还有可以传入Callback
,Looper
等的构造函数。
public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}
复制代码
Message的复用
通过handler.obtainMessage
而不是new
方式获得消息实例。因为obtainMessage
方法会先检测消息池是否有可以复用的消息,没有再去new
一个消息实例。下面是类Message的obtain
方法。
public static Message obtain() {synchronized (sPoolSync) {if (sPool != null) {Message m = sPool;sPool = m.next;m.next = null;m.flags = 0; // clear in-use flagsPoolSize--;return m;}}return new Message();}
复制代码
sPool
的类型是Message
,内部通过成员变量next
,维护一个消息池。虽然叫消息池,内部却通过next
不断的指向下一个Message
,以链表维护的这个消息池,默认大小为50。在链表sPool
不为空的情况,取表头Message
元素,并将相关属性进行初始化。
那么Message对象是在什么时候被放进消息池中的呢?
在Looper
的loop
方法中,最后调用Message
的recycleUnchecked
方法
void recycleUnchecked() {// Mark the message as in use while it remains in the recycled object pool.// Clear out all other details.flags = FLAG_IN_USE;what = 0;arg1 = 0;arg2 = 0;obj = null;replyTo = null;sendingUid = -1;when = 0;target = null;callback = null;data = null;synchronized (sPoolSync) {if (sPoolSize < MAX_POOL_SIZE) {next = sPool;sPool = this;sPoolSize++;}}}
复制代码
在同步代码块,可以看到,将sPool
指向当前要被回收的Message
对象,而Message
的next
指向之前的表头。
总结
- 在子线程使用
Handler
,需要先调用Looper.prepare
方法,再调用Looper.loop
方法。 - 消息队列以链表的形式维护着,消息的存放和获取顺序根据
when
时间依次排列。 - 通过Handler,在子线程耗时操作,主线程更新UI。应用场景:
IntentService
、HandlerTread
、AsyncTack
。
知识点分享
HandlerThread必知必会
IntentService必知必会
如果觉得文章有用,给文章点个赞,铁子
Android:Handler的消息机制相关推荐
- [Android]Handler的消息机制
最经面试中,技术面试中有一个是Handler的消息机制,细细想想,我经常用到的Handler无非是在主线程(或者说Activity)新建一个Handler对象,另外一个Thread是异步加载数据,同时 ...
- 图解 Android Handler 线程消息机制
从现实生活中理解线程消息机制 android 有一种叫消息队列的说法,这里我们可以这样理解:假如一个隧道就是一个消息队列,那么里面的每一部汽车就是一个一个消息,这里我们先忽略掉超车等种种因素,只那么先 ...
- 浅析Android中的消息机制
在分析Android消息机制之前,我们先来看一段代码: [java] view plaincopy public class MainActivity extends Activity impleme ...
- Android中的消息机制
Android 中的消息机制其实就是指的是 Handler 消息机制以及附带的 Looper 和 MessageQueue 的工作流程. 1.Android 为什么提供Handler? 解决子线程不能 ...
- 重温Android中的消息机制
引入: 提到Android中的消息机制,大家应该都不陌生,我们在开发中不可避免的要和它打交道.从我们开发的角度来看,Handler是Android消息机制的上层接口.我们在平时的开发中只需要和Hand ...
- android return 如何跳出两个循环_关于不得不学的Android知识之消息机制
概述 相信不管是出入Android,还是已开发多年的老司机们,肯定都对Android的Handler不会陌生,而它就是今天要介绍的Android消息机制中的一部分.在Android系统中,有两大特色利 ...
- Android Handler 异步消息处理机制的妙用 创建强大的图片载入类
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38476887 ,本文出自[张鸿洋的博客] 近期创建了一个群.方便大家交流,群号: ...
- Android 线程与消息 机制 15问15答
1.handler,looper,messagequeue三者之间的关系以及各自的角色? 答:MessageQueue就是存储消息的载体,Looper就是无限循环查找这个载体里是否还有消息.Handl ...
- Android中的消息机制:Handler消息传递机制
参考<疯狂android讲义>第2版3.5 P214 一.背景 出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发操作UI组件,可能导致线程安全问题.为 ...
最新文章
- fushioncharts破解
- Saiku_学习_01_saiku安装与运行
- 如何让引擎蜘蛛天天光临你的网站
- 中国最大的python社区-python 最大堆
- GPT v.s. 中国象棋:写过文章解过题,要不再来下盘棋?
- CDN预热与刷新在促销活动中的应用
- 使用cocoaPods一键集成第三方登录(新浪微博,qq,微信)
- C++ int型与char型辨析
- spring mvc 解决csrf跨站请求攻击
- 《遥感原理与应用》总结—遥感概论
- QT遍历 Json 根节点
- 红米手机android无法开机画面,红米手机一直停在开机画面怎么办?
- .Net Core WebApi 模型验证的处理
- PPT另存为PDF图片去白边
- java x的平方怎么打出来_java 中的输入输出
- R语言包翻译——翻译
- Java中的代码点和代码单元(转)
- va和tn玩游戏哪个好 va和tn哪个伤眼睛
- 明亮如星研旅(4)—— Online and offline handwritten Chinese character recognition
- APP登录注册 步骤三:客户端