Android Handler消息机制源码解析

好记性不如烂笔头,今天来分析一下Handler的源码实现

Handler机制是Android系统的基础,是多线程之间切换的基础。下面我们分析一下Handler的源码实现。

Handler消息机制有4个类合作完成,分别是Handler,MessageQueue,Looper,Message Handler : 获取消息,发送消息,以及处理消息的类 MessageQueue:消息队列,先进先出 Looper : 消息的循环和分发 Message : 消息实体类,分发消息和处理消息的就是这个类

主要工作原理就是: Looper 类里面有一个无限循环,不停的从MessageQueue队列中取出消息,然后把消息分发给Handler进行处理

先看看在子线程中发消息,去在主线程中更新,我们就在主线程中打印一句话。

第一步: 在MainActivity中有一个属性uiHandler,如下:

1 Handler uiHandler = new Handler(){ 2 @Override 3 public void handleMessage(Message msg) { 4 super.handleMessage(msg); 5 if(msg.what == 100){ 6 Log.d("TAG","我是线程1 msg.what=" + msg.what + " msg.obj=" + msg.obj.toString()); 7 }else if(msg.what == 200){ 8 Log.d("TAG","我是线程2 msg.what=" + msg.what + " msg.obj=" + msg.obj.toString()); 9 } 10 } 11 };

创建一个Handler实例,重写了handleMessage方法。根据message中what的标识来区别不同线程发来的数据并打印

第二步: 在按钮的点击事件中开2个线程,分别在每个线程中使用 uiHandler获取消息,并发送消息。如下

1 2 findViewById(R.id.tv_hello).setOnClickListener(new View.OnClickListener() { 3 @Override 4 public void onClick(View v) { 5 //线程1 6 new Thread(new Runnable() { 7 @Override 8 public void run() { 9 //1 获取消息 10 Message message = uiHandler.obtainMessage(); 11 message.what = 100; 12 message.obj = "hello,world"; 13 14 //2 分发消息 15 uiHandler.sendMessage(message); 16 } 17 }).start(); 18 19 //线程2 20 new Thread(new Runnable() { 21 @Override 22 public void run() { 23 //1 获取消息 24 Message message = uiHandler.obtainMessage(); 25 message.what = 200; 26 message.obj = "hello,android"; 27 28 //2 分发消息 29 uiHandler.sendMessage(message); 30 } 31 }).start(); 32 } 33 });

使用很简单,两步就完成了从子线程把数据发送到主线程并在主线程中处理 我们来先分析Handler的源码

Handler 的源码分析

Handler的构造函数

1 public Handler() { 2 this(null, false); 3 }

调用了第两个参数的构造函数,如下

1 public Handler(Callback callback, boolean async) { 2 //FIND_POTENTIAL_LEAKS 为 false, 不走这块 3 if (FIND_POTENTIAL_LEAKS) { 4 final Class<? extends Handler> klass = getClass(); 5 if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && 6 (klass.getModifiers() & Modifier.STATIC) == 0) { 7 Log.w(TAG, "The following Handler class should be static or leaks might occur: " + 8 klass.getCanonicalName()); 9 } 10 } 11 12 mLooper = Looper.myLooper(); 13 if (mLooper == null) { 14 throw new RuntimeException( 15 "Can't create handler inside thread " + Thread.currentThread() 16 + " that has not called Looper.prepare()"); 17 } 18 mQueue = mLooper.mQueue; 19 mCallback = callback; 20 mAsynchronous = async; 21 }

主要是下面几句: mLooper = Looper.myLooper(); 调用Looper的静态方法获取一个Looper 如果 mLooper == null ,就会抛出异常 Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"; 说明我们的线程中如果没有一个looper的话,直接 new Handler() 是会抛出这个异常的。必须首先调用 Looper.prepare(),这个等下讲Looper的源码时就会清楚了。

接下来,把 mLooper中的 mQueue赋值给Handler中的 mQueue,callback是传出来的值,为null 这样我们的Handler里面就保存了一个Looper变量,一个MessageQueue消息队列.

接下来就是 Message message = uiHandler.obtainMessage();

obtainMessage()的源码如下:

1 public final Message obtainMessage() 2 { 3 //注意传的是一个 this, 其实就是 Handler本身 4 return Message.obtain(this); 5 }

又调用了Message.obtain(this);方法,源码如下:

1public static Message obtain(Handler h) { 2 //1 调用obtain()获取一个Message实例m 3 Message m = obtain(); 4 5 //2 关键的这句,把 h 赋值给了消息的 target,这个target肯定也是Handler了 6 m.target = h; 7 8 //3 返回 m 9 return m; 10 }

这样,获取的消息里面就保存了 Handler 的实例。 我们随便看一下 obtain() 方法是如何获取消息的。如下

1 public static Message obtain() { 2 //sPoolSync同步对象用的 3 synchronized (sPoolSync) { 4 //sPool是Message类型,静态变量 5 if (sPool != null) { 6 //就是个单链表,把表头返回,sPool再指向下一个 7 Message m = sPool; 8 sPool = m.next; 9 m.next = null; 10 m.flags = 0; // clear in-use flag 11 sPoolSize--; 12 return m; 13 } 14 } 15 16 //如果sPool为空,则直接 new 一个 17 return new Message(); 18 }

obtain()获取消息就是个享元设计模式,享元设计模式用大白话说就是: 池中有,就从池中返回一个,如果没有,则新创建一个,放入池中,并返回。

使用这种模式可以节省过多的创建对象。复用空闲的对象,节省内存。

最后一句发送消息uiHandler.sendMessage(message);源码如下:

1 public final boolean sendMessage(Message msg) 2 { 3 return sendMessageDelayed(msg, 0); 4 }

sendMessageDelayed(msg, 0) 源码如下

1 public final boolean sendMessageDelayed(Message msg, long delayMillis) 2 { 3 if (delayMillis < 0) { 4 delayMillis = 0; 5 } 6 return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); 7 }

又调用了sendMessageAtTime() 源码如下:

1 public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 2 // Handler中的mQueue,就是前面从Looper.get 3 MessageQueue queue = mQueue; 4 if (queue == null) { 5 RuntimeException e = new RuntimeException( 6 this + " sendMessageAtTime() called with no mQueue"); 7 Log.w("Looper", e.getMessage(), e); 8 return false; 9 } 10 return enqueueMessage(queue, msg, uptimeMillis); 11 }

调用enqueueMessage()

1 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { 2 //注意这句,如果我们发送的消息不是 uiHandler.obtainMessage()获取的,而是直接 new Message()的,这个时候target为null 3 //在这里,又把this 给重新赋值给了target了,保证不管怎么获取的Message,里面的target一定是发送消息的Handler实例 4 msg.target = this; 5 6 // mAsynchronous默认为false,不会走这个 7 if (mAsynchronous) { 8 msg.setAsynchronous(true); 9 } 10 return queue.enqueueMessage(msg, uptimeMillis); 11 }

最后调用queue.enqueueMessage(msg, uptimeMillis)源码如下:

1 boolean enqueueMessage(Message msg, long when) { 2 if (msg.target == null) { 3 throw new IllegalArgumentException("Message must have a target."); 4 } 5 if (msg.isInUse()) { 6 throw new IllegalStateException(msg + " This message is already in use."); 7 } 8 9 synchronized (this) { 10 if (mQuitting) { 11 IllegalStateException e = new IllegalStateException( 12 msg.target + " sending message to a Handler on a dead thread"); 13 Log.w(TAG, e.getMessage(), e); 14 msg.recycle(); 15 return false; 16 } 17 18 msg.markInUse(); 19 msg.when = when; 20 Message p = mMessages; 21 boolean needWake; 22 if (p == null || when == 0 || when < p.when) { 23 // New head, wake up the event queue if blocked. 24 msg.next = p; 25 mMessages = msg; 26 needWake = mBlocked; 27 } else { 28 // Inserted within the middle of the queue. Usually we don't have to wake 29 // up the event queue unless there is a barrier at the head of the queue 30 // and the message is the earliest asynchronous message in the queue. 31 needWake = mBlocked && p.target == null && msg.isAsynchronous(); 32 Message prev; 33 for (;;) { 34 prev = p; 35 p = p.next; 36 if (p == null || when < p.when) { 37 break; 38 } 39 if (needWake && p.isAsynchronous()) { 40 needWake = false; 41 } 42 } 43 msg.next = p; // invariant: p == prev.next 44 prev.next = msg; 45 } 46 47 // We can assume mPtr != 0 because mQuitting is false. 48 if (needWake) { 49 nativeWake(mPtr); 50 } 51 } 52 return true; 53 }

enqueue单词的英文意思就是 排队,入队的意思。所以enqueueMessage()就是把消息进入插入单链表中,上面的源码可以看出,主要是按照时间的顺序把msg插入到由单链表中的第一个位置中,接下来我们就需要从消息队列中取出msg并分了处理了。这时候就调用Looper.loop()方法了。

Looper.loop()的源码我简化了一下,把主要的流程留下,方法如下:

1 public static void loop() { 2 final Looper me = myLooper(); 3 if (me == null) { 4 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 5 } 6 final MessageQueue queue = me.mQueue; 7 8 9 for (;;) { 10 Message msg = queue.next(); // might block 11 if (msg == null) { 12 // No message indicates that the message queue is quitting. 13 return; 14 } 15 16 msg.target.dispatchMessage(msg); 17 18 19 msg.recycleUnchecked(); 20 } 21 }

可以看到,loop()方法就是在无限循环中不停的从queue中拿出下一个消息 然后调用 msg.target.dispatchMessage(msg) , 上文我们分析过,Message的target保存的就是发送的Handler实例,这我们的这个demo中,就是uiHandler对象。

说白了就是不停的从消息队列中拿出一个消息,然后发分给Handler的dispatchMessage()方法处理。

Handler的dispatchMessage()方法源码如下:

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 }

可以看到,一个消息分发给dispatchMessage()之后 1 首先看看消息的callback是否为null,如果不为null,就交给消息的handleCallback()方法处理,如果为null

2 再看看Handler自己的mCallback是否为null,如果不为null,就交给mCallback.handleMessage(msg)进行处理,并且如果返回true,消息就不往下分发了,如果返回false

3 就交给Handler的handleMessage()方法进行处理。

有三层拦截,注意,有好多插件化在拦截替换activity的时候,就是通过反射,把自己实例的Handler实例赋值通过hook赋值给了ActivityThread相关的变量中,并且mCallback不为空,返回了false,这样不影响系统正常的流程,也能达到拦截的目的。说多了。

前面分析了handler处理消息的机制,也提到了Looper类的作用,下面我们看看Looper的源码分析

Looper源码分析

我们知道,APP进程的也就是我们应用的入口是ActivityThread.main()函数。 对这块不熟悉的需要自己私下补课了。

ActivityThread.main()的源码同样经过简化,如下:

文件位于 /frameworks/base/core/java/android/app/ActivityThread.java

1public static void main(String[] args) { 2 //1 创建一个looper 3 Looper.prepareMainLooper(); 4 5 //2 创建一个ActivityThread实例并调用attach()方法 6 ActivityThread thread = new ActivityThread(); 7 thread.attach(false, startSeq); 8 9 //3 消息循环 10 Looper.loop(); 11}

可以看到,主线程中第一句就是创建一个looper,并调用了Looper.loop()进行消息循环,因为线程只有有了一个looper,才能消息循环,才能不停的从消息队列中取出消息,分发消息,并处理消息。没有消息的时候就阻塞在那,等待消息的到来并处理,这样的我们的app就是通过这种消息驱动的方式运行起来了。

我们来看下 Looper.prepareMainLooper() 的源码,如下

1 public static void prepareMainLooper() { 2 prepare(false); 3 synchronized (Looper.class) { 4 if (sMainLooper != null) { 5 throw new IllegalStateException("The main Looper has already been prepared."); 6 } 7 sMainLooper = myLooper(); 8 } 9 }

第一句,调用了prepare(false) 源码如下:

1 private static void prepare(boolean quitAllowed) { 2 //1 查看当前线程中是否有looper存在,有就抛个异常 3 //这表明,一个线程只能有一个looper存在 4 if (sThreadLocal.get() != null) { 5 throw new RuntimeException("Only one Looper may be created per thread"); 6 } 7 8 //2 创建一个Looper并存放在sThreadLocal中 9 sThreadLocal.set(new Looper(quitAllowed)); 10 }

sThreadLocal的定义如下 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

是一个静态的变量。整个APP进程中只有一个sThreadLocal,sThreadLocal是线程独有的,每个线程都调用sThreadLocal保存,关于sThreadLocal的原理,其实就是类似HashMap(当然和HashMap是有区别的),也是key,value保存数据,只不过key就是sThreadLocal本身 ,但是映射的数组却是每个线程中独有的,这样就保证了sThreadLocal保存的数据每个线程独有一份,关于ThreadLocal的源码分析,后面几章会讲。

既然Looper和线程有关,那么我们来看下Looper类的定义,源码如下:

1/** 2 * Class used to run a message loop for a thread. Threads by default do 3 * not have a message loop associated with them; to create one, call 4 * {@link #prepare} in the thread that is to run the loop, and then 5 * {@link #loop} to have it process messages until the loop is stopped. 6 * 7 * <p>Most interaction with a message loop is through the 8 * {@link Handler} class. 9 * 10 * <p>This is a typical example of the implementation of a Looper thread, 11 * using the separation of {@link #prepare} and {@link #loop} to create an 12 * initial Handler to communicate with the Looper. 13 * 14 * <pre> 15 * class LooperThread extends Thread { 16 * public Handler mHandler; 17 * 18 * public void run() { 19 * Looper.prepare(); 20 * 21 * mHandler = new Handler() { 22 * public void handleMessage(Message msg) { 23 * // process incoming messages here 24 * } 25 * }; 26 * 27 * Looper.loop(); 28 * } 29 * }</pre> 30 */ 31public final class Looper { 32 ....... 33}

我们看上面的注释

1 * <pre> 2 * class LooperThread extends Thread { 3 * public Handler mHandler; 4 * 5 * public void run() { 6 * Looper.prepare(); 7 * 8 * mHandler = new Handler() { 9 * public void handleMessage(Message msg) { 10 * // process incoming messages here 11 * } 12 * }; 13 * 14 * Looper.loop(); 15 * } 16 * }</pre>

这就是经典的Looper的用法 ,可以在一个线程中开始处调用 Looper.prepare(); 然后在最后调用 Looper.loop();进行消息循环,可以把其它线程中的Handler实传进来,这样,一个Looper线程就有了,可以很方便的切换线程了。 下章节我们来自己设计一个Looper线程,做一些后台任务。

Handler的消息机制源码就分析到这了

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )