[Android] Handler源码解析 (Java层)
之前写过一篇文章,概述了Android应用程序消息处理机制。本文在此文基础上,在源码级别上展开进行概述
简单用例
Handler的使用方法如下所示:
Handler myHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { ... }} };class myThread implements Runnable { public void run() { while (!Thread.currentThread().isInterrupted()) { Message message = Message.obtain(); message.what = TestHandler.GUIUPDATEIDENTIFIER;TestHandler.this.myHandler.sendMessage(message); message.recycle();try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } }
或者:
mHandler=new Handler();
mHandler.post(new Runnable(){void run(){...}
});
又或者:
class LooperThread extends Thread {public Handler mHandler;public void run() {Looper.prepare();mHandler = new Handler() {public void handleMessage(Message msg) {// process incoming messages here}};Looper.loop();}
}
源码解析
首先看其构造函数:
new Handler()
...
public Handler() {this(null, false);
}
...
public Handler(Looper looper, Callback callback) {this(looper, callback, false);
}
...
public Handler(Callback callback, boolean async) {if (FIND_POTENTIAL_LEAKS) { // 默认为false,若为true则会检测当前handler是否是静态类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());}}// 1. 获得当前线程的loopermLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}//2. 获得looper上的message queuemQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async;
}
由此引入了两个关键对象Looper和MessageQueue。
先看 mLooper = Looper.myLooper();
这一句发生了什么:
public static Looper myLooper() {return sThreadLocal.get();
}
可以看到,该方法返回一个sThreadLocal对象中保存的Looper。关于ThreadLocal类,请参考这里,本文不展开。
如果尚未在当前线程上运行过Looper.prepare()的话,myLooper会返回null。接下来看看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对象,并将其保存在sThreadLocal中。接下来看一下Looper的构造函数。
private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();
}
调用完Looper.prepare()后,需调用Looper.loop()才能使消息循环运作起来,其源码如下所示:
public static void loop() {final Looper me = myLooper(); //1. 取出looper对象if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}final MessageQueue queue = me.mQueue; //2. 取出looper绑定的message queue// 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();// loop time.long tm = 0;...for (;;) {Message msg = queue.next(); // 3. 堵塞式在message queue中取数据if (msg == null) {// No message indicates that the message queue is quitting.return;}...msg.target.dispatchMessage(msg); 4. 分发message到指定的target handler...// Make sure that during the course of dispatching the// identity of the thread wasn't corrupted.final long newIdent = Binder.clearCallingIdentity();if (ident != newIdent) {...}msg.recycleUnchecked(); // 5. 回收message对象...}
}
可以简单地将Looper.loop()理解成一个不断检测message queue是否有数据,若有即取出并执行回调的死循环。 接下来看一下Message类:
public final class Message implements Parcelable {public int what;public int arg1;public int arg2;public Object obj;.../*package*/ int flags;/*package*/ long when;/*package*/ Bundle data;/*package*/ Handler target;/*package*/ Runnable callback;/*package*/ Message next;private static final Object sPoolSync = new Object();private static Message sPool;private static int sPoolSize = 0;
}
what、arg1、arg2这些属性本文不作介绍,我们把目光集中在next、sPoolSync、sPool、sPoolSize这四个静态属性上。
当我们调用Message.obtain()时,返回了一个Message对象。Message对象使用完毕后,调用recycle()方法将其回收。其中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();
}
可以看到,obtain方法被调用时,首先检测sPool对象是否为空,若否则将其当做新的message对象返回,并“指向"message对象的next属性,sPoolSize自减。可以看出message对象通过next属性串成了一个链表,sPool为“头指针”。再来看看recycle方法的实现:
public void recycle() {if (isInUse()) {if (gCheckRecycle) {throw new IllegalStateException("This message cannot be recycled because it is still in use.");}return;}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++;}}
}
如果message对象不是处于正在被使用的状态,则会被回收。其属性全部恢复到原始状态后,放在了链表的头部。sPool对象“指向”它,sPoolSize自增。
综上可以看出,通过obtain和recycle方法可以重用message对象。通过操作next、sPoolSync、sPool、sPoolSize这四个属性,实现了一个类似栈的对象池。
msg.target为handler类型,即向handler成员的dispatchMessage方法传入msg参数,其实现如下所示:
public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}
}
这里可以看到回调了各种接口。
到目前为止,我们知道了如何处理在消息队列里面的msg对象,但仍不知道msg对象是如何放到消息队列里面的。通常来说,我们通过Handler的sendMessage(msg)方法来发送消息,其源码如下所示:
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) {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);
}
可知sendMessage最终会调用queue.enqueueMessage(msg, uptimeMillis)将msg对象保存至message queue中,uptimeMillis表示msg执行回调的时刻。 我们来看一下MessageQueue类的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("MessageQueue", e.getMessage(), e);msg.recycle();return false;}// 1.设置当前msg的状态msg.markInUse();msg.when = when;Message p = mMessages;boolean needWake;// 2.检测当前头指针是否为空(队列为空)或者没有设置when 或者设置的when比头指针的when要前if (p == null || when == 0 || when < p.when) {// 3. 插入队列头部,并且唤醒线程处理msgmsg.next = p;mMessages = msg;needWake = mBlocked;} else {//4. 几种情况要唤醒线程处理消息:1)队列是堵塞的 2)barrier,头部结点无target 3)当前msg是堵塞的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;}}// 5. 将当前msg插入第一个比其when值大的结点前。msg.next = p; // invariant: p == prev.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;
}
结合注释,我们可以了解到msg push到queue中时,queue的状态的变化和处理队列的逻辑。
前文中Looper对象的loop方法中:
for (;;) {...Message msg = queue.next(); // 3. 堵塞式在message queue中取数据if (msg == null) {// No message indicates that the message queue is quitting.return;}...msg.target.dispatchMessage(msg); 4. 分发message到指定的target handler...
}
可以看出,message queue的next方法被调用时,可能会发生堵塞。我们来看一看message queue的next方法:
Message next() {// 1. 判断当前loop是否已经使用过,下文会解释这个mPtrfinal long ptr = mPtr;if (ptr == 0) {return null;}int pendingIdleHandlerCount = -1; // -1 only during first iterationint nextPollTimeoutMillis = 0;// 2. 进入死循环,直到获取到合法的msg对象为止。for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands(); // 这个是什么?}// 3. 进入等待,nextPollTimeoutMillis为等待超时值nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {// 4. 获取下一个msgfinal long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;if (msg != null && msg.target == null) {// 当前节点为barrier,所以要找到第一个asynchronous节点do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous());}if (msg != null) {if (now < msg.when) {// 当前队列里最早的节点比当前时间还要晚,所以要进入堵塞状态,超时值为nextPollTimeoutMillisnextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);} else {// 删除当前节点,并返回mBlocked = false;if (prevMsg != null) {prevMsg.next = msg.next;} else {mMessages = msg.next;}msg.next = null;if (false) Log.v("MessageQueue", "Returning message: " + msg);return msg;}} else {// 头结点指向nullnextPollTimeoutMillis = -1;}// Process the quit message now that all pending messages have been handled.if (mQuitting) {dispose();return null;}// 5. 如果当前状态为idle(就绪),则进入idle handle的代码块// 进入idle的情况有:队列为空;队列头元素blocking;if (pendingIdleHandlerCount < 0&& (mMessages == null || now < mMessages.when)) {pendingIdleHandlerCount = mIdleHandlers.size();}if (pendingIdleHandlerCount <= 0) {// 6. 本轮唤醒(next被调用)时没处理任何东西,故再次进入等待。mBlocked = true;continue;}if (mPendingIdleHandlers == null) {mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];}mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);}// 在一次next调用中,这个代码块只会执行一次for (int i = 0; i < pendingIdleHandlerCount; i++) {final IdleHandler idler = mPendingIdleHandlers[i];mPendingIdleHandlers[i] = null; // release the reference to the handlerboolean keep = false;try {// 如果返回true,则idler被保留,下次next的idle时会被调用。keep = idler.queueIdle();} catch (Throwable t) {Log.wtf("MessageQueue", "IdleHandler threw exception", t);}if (!keep) {synchronized (this) {mIdleHandlers.remove(idler);}}}// Reset the idle handler count to 0 so we do not run them again.pendingIdleHandlerCount = 0;// While calling an idle handler, a new message could have been delivered// so go back and look again for a pending message without waiting.nextPollTimeoutMillis = 0;}
}
代码执行流程见注释。其中IdleHandler是一个接口:
public static interface IdleHandler {boolean queueIdle();
}
IdleHandler提供了一个在MessageQueue进入idle时的一个hook point。更多时与barrier机制一起使用,使message queue遇到barrier时产生一个回调。
总结
前面涉及到的几个主要的类Handler、Looper、MessageQueue和Message的关系如下所述:
Handler负责将Looper绑定到线程,初始化Looper和提供对外API。
Looper负责消息循环和操作MessageQueue对象。
MessageQueue实现了一个堵塞队列。
Message是一次业务中所有参数的载体。
框架图如下所示:
+------------------+| Handler |+----+--------^----+| |send | | dispatch| |v |+----- <---+| || Looper || || |+---> -----+| ^enqueue| | next| |+--------v------+----------+| MessageQueue |+--------+------^----------+| |nativePollOnce | | nativeWake| |
+-----------------v------+---------------------+Lower Layer
最后,留意到MessageQueue中有4个native方法:
// 初始化和销毁
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
// 等待和唤醒
private native static void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
// 判断native层的状态
private native static boolean nativeIsIdling(long ptr);
将会在后续文章中进行介绍。
[Android] Handler源码解析 (Java层)相关推荐
- Handler 源码解析(Java 层)
本文由船员 ChangeHui 自荐,转载发布 从很早开始就认识到 Handler 了,只不过那时修为尚浅,了解的不够深刻,也没有应用自如.不过随着工作时间的增长,对 Handler 又有了更深层次 ...
- Handler源码分析 - Java层
Handler最常见的使用场景就是下载回调,为了不影响用户体验Android不支持在主线程中进行耗时时操作,长时间的耗时操作会产生ANR异常,而下载无疑是耗时操作,所以我们会在子线程中进行下载.但,下 ...
- Handler 源码解析——Handler的创建
前言 Android 提供了Handler和Looper来来满足线程间的通信,而前面我们所说的IPC指的是进程间的通信.这是两个完全不同的概念. Handler先进先出原则,Looper类用来管理特定 ...
- Handler全家桶之 —— Handler 源码解析
本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 本文首发于本人简书 前言 好记性不如烂笔头. 这是一个系列文章,将会包括: Handler全家桶之 -- Handler 源码解析 ...
- android sdk 源码解析
AndroidSdkSourceAnalysis:https://github.com/LittleFriendsGroup/AndroidSdkSourceAnalysis 第一期 Class 分析 ...
- Android Lifecycle源码解析(一)
Android Lifecycle源码解析(一) 首先我们看HomeActivity中我们添加到一行代码 public class HomeActivity extends AppCompatActi ...
- 【Android】Android Broadcast源码解析
Android Broadcast源码解析 一.静态广播的注册 静态广播是通过PackageManagerService在启动的时候扫描已安装的应用去注册的. 在PackageManagerServi ...
- Android xUtils3源码解析之图片模块
本文已授权微信公众号<非著名程序员>原创首发,转载请务必注明出处. xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3 ...
- Android xUtils3源码解析之注解模块
本文已授权微信公众号<非著名程序员>原创首发,转载请务必注明出处. xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3 ...
最新文章
- vue全家桶 ---axios的使用和二次封装
- 人脸识别技术大总结(1)——Face Detection Alignment
- 【EventBus】事件通信框架 ( 订阅方法注册 | 检查订阅方法缓存 | 反射获取订阅类中的订阅方法 )
- bzoj 1011 近似估计
- 关于SWT中的布局Layout
- android 活动说明,Android – 如何发送GCM推送通知以及要加载哪些活动的说明?
- 吊打一切现有开源OCR项目:效果再升7%,速度提升220%
- JSP的4大域对象及范围(简)
- 如果把Python代码写成这样子就太难看了
- 1、视觉slam简介
- MKV(Matroska)常见问题浅析
- Java读取TXT文件中文输出乱码
- MKS-DLC雕刻MKS_TFT_CNC字机器,CNC雕刻,激光雕刻GRBL使用方法
- #九、江恩、四维理论方法从古人那里得到的启发(一)来自星空的启示
- Aho-Corasick懵逼学习
- 【数据分析 R语言实战】学习笔记 第六章 参数估计与R实现(上)
- 用c语言做记忆测试小游戏,用C语言实现简单小游戏
- bu薪水 华为cloud_【华为】CLOUDBU 华为云数据库工程师招聘 - 软件与微电子学院(SSM)版 - 北大未名BBS...
- 长URL链接转短链接算法
- python爬取煎蛋网妹子图
热门文章
- python运维之轻松模拟开发FTP软件05
- Zabbix Linux 客户端安装
- vba移动文件_Excel VBA 之 按需求移动、复制文件
- 基于ARP的网络扫描工具netdiscover常用命令集合大学霸IT达人
- Metasploit Shell升级Meterpreter会话技巧
- XamarinSQLite教程下载安装SQLite/SQL Server Compact Toolbox
- SQLite中的SELECT子句
- Xamarin XAML语言教程模板视图TemplatedView(一)
- iOS 9应用开发教程之iOS 9新特性
- linux中if语句s,linux 中 if 语句条件的含义