Android 从源码角度分析消息处理机制(Handler,Looper,Message)
来源:互联网 发布:linux 统计登录次数 编辑:程序博客网 时间:2024/05/01 05:14
Android 从源码角度分析消息处理机制(Handler,Looper,Message)
前言
在Android中,修改UI的操作必须要放入到主线程中。而我们的网络请求往往是长时操作,需要放入到子线程进行请求。可以通过Handler
实现不同线程间的通信。对于如何实现的,网上有很多的教程或博文,也解释的非常清楚,这里不在多叙。
但在使用过程中,我们可能会有一些疑问:
为什么可以通过
Handler
实现不同的线程间通信。通过
sendXXX()
方法,是怎么在handlerMessage()
中回调的为什么主线程不需要
Looper.prepare()
方法,而子线程需要Looper.prepare()
和Looper.loop()
方法。
可能很多博客都会从Handler
,Looper
,MessageQueue
他们之间的联系上,去解释Android 中的消息处理机制,本篇博客意在从源码的角度分析其通信原理。
源码分析
在通常的编码中,onCreate()
方法是一个Activity
的入口,我们往往在这里做一些初始化操作。而对于一个程序来说,ActivityThread
的main()
方法,便是一个应用的入口,所以我们先看一下main()
方法的源码
public static void main(String[] args) { // 省略.. // 方法一,构造Looper 对象。 Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } // 省略... // 方法二,开始消息队列的循环 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
在main()
方法中,存在两个关键性的方法,分别是方法一和方法二。从英文命名上,我们可以大致猜出他们的意思,分别是准备主线程的Looper
对象和开始循环消息队列。
进入到Looper.prepareMainLooper()
方法中,
public static void prepareMainLooper() { // 构造Looper 方法 prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } // 获取当前的Looper()对象,进行赋值到sMainLooper sMainLooper = myLooper(); } }
prepare(false)
方法构造属于当前线程的Looper
对象,传入参数暂且不管,稍后会看其源码。
然后判断sMainLooper
对象是否为null,如果不为null就会抛出异常,异常的内容“主线程的Looper对象已经准备”,在这里,通过字段的命名可以看出sMainLooper
是Looper
对象的静态字段,并且保存主线程的Looper
对象。
那么从prepareMainLooper()
和判断条件可以得出,构造主线程的Looper
对象,保存对象到sMainLooper
中。如果sMainLooper
不为null,表明重复调用,抛出异常。
那么进入到prepare(false)
方法中,
private static void prepare(boolean quitAllowed) { // 判断是否创建过Looper 对象 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } // 创建Looper 对象,并保存 sThreadLocal.set(new Looper(quitAllowed)); }
先是通过sThreadLocal.get()
方法获取当前线程的Looper
对象,如果不为null,则抛出异常“一个线程只能创建一个Looper对象。”
如果不为null,则创建Looper
对象并保存。
那么sThreadLoacl
又是什么呢,从命名上可以看出他是一个静态字段,看一下声明,
// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
ThreadLocal
类型,该类是java.lang
中。看一下他的get()
和set()
方法,因为我们主要是通过这两个方法获取对应的Looper
对象。
// get 方法 public T get() { // 获取当前所在的线程,很关键 Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values != null) { Object[] table = values.table; int index = hash & values.mask; if (this.reference == table[index]) { return (T) table[index + 1]; } } else { values = initializeValues(currentThread); } return (T) values.getAfterMiss(this); } public void set(T value) { // 获取当前的线程 Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value); }
这两个方法,看起来有点晕晕的,我们不深究其细节实现,明白其功能即可。
get()
能够根据当前线程返回对应的Looper
对象
set()
能够根据当前线程保存对应的Looper
对象。
继续回到prepare()
方法中,我们通过get()
判断,通过set()
进行保存。其中通过new Looper(quitAllowed)
方法,构造Looper
对象,看一下他的实现
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
在Looper
的构造方法中,构造了一个MessageQueue()
对象并保存,同时保存当前线程的对象。那么关键词出现了,MessageQueue,这时网上很多分析中出现的关键类,负责消息的队列。由此可以看出,Looper
对象中保存有MessageQueue
的实例。
在这里,我们需要注意的是mQueue
和mThread
,从命名可以看出都是成员变量,即他和Looper
的实例都是一一对应的。
public final class Looper { static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static Looper sMainLooper; // guarded by Looper.class // 每一个looper 对应的消息队列 final MessageQueue mQueue; // looper 对应的线程 final Thread mThread;}
将字段的具体声明贴出,以便理解。
那么,到此,整个Looper.prepareMainLooper()
方法就大致分析完了。
Looper.loop()
在ActivityThread.main()
中,最后调用了Looper.loop()
方法,看一下此方法的实现。
public static void loop() { // 1.获取Looper 对象 final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } // 2.获取Looper对象对应的消息队列 final MessageQueue queue = me.mQueue; // 3.无线循环,遍历消息队列 for (;;) { Message msg = queue.next(); // 4.循环消息队列 if (msg == null) { // No message indicates that the message queue is quitting. return; } msg.target.dispatchMessage(msg);// 5.分发消息 msg.recycleUnchecked(); // 6.回收资源 } }
中间省略了一些代码,自上往下分析:
1的地方首先获取到当前线程的Looper
对象。
2的时候获取到Looper
对象的MessageQueue
实例,在创建Looper
时,构造方法中创建了和他一一对应的MessageQueue
对象,主要是作为消息队列。
然后无限for循环,遍历消息队列。
通过MessageQueue
的next()
方法获取到Message
对象,具体的5和6,我们需要看一下Handler
的实现后进行理解。
Handler的实现
通过上面的分析,我们知道在主线程中构造了Looper
对象并且开始轮询。那么另一个关键人物Handler
,势必要分析他们直接的联系。
看一下Handler
的构造方法
public Handler(Callback callback, boolean async) { // ... // 获取当前线程的Looper对象 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } // 保存Looper对象的消息队列 mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
通常的使用,都是直接使用new Handler()
,最终仍会调用此构造方法,默认为null和false。
在这个方法中,handler
保存了当前线程的looper
的实例和消息队列。
通常的使用中通过handler.sendMessage()
向主线程发送消息。看一下实现
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); }
最终会调用此方法,在这个方法中,获取消息队列,并调用enqueueMessage(queue, msg, uptimeMillis)
,看一下这个方法,
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { // msg.target = handler msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
该方法,将当前handler
对象赋值到msg.target
中,并且调用MessageQueue
的enqueueMessage()
方法。
到此,对Handler
的分析就差不多结束了。回过头,在ActivityThread.main()
中,Looper.loop()
我们还有一点没有分析,就是无限循环的处理,再看一下源码
// 3.无线循环,遍历消息队列 for (;;) { Message msg = queue.next(); // 4.循环消息队列 if (msg == null) { // No message indicates that the message queue is quitting. return; } msg.target.dispatchMessage(msg);// 5.分发消息 msg.recycleUnchecked(); // 6.回收资源 }
我们知道,msg.targer
是我们构造的handler
,那么绕了一圈有回到了自身的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); } }
其中由几个判断,最终不通过的时候调用handlerMessage(msg)
方法。看一下之前的判断
msg.callback
指定的是什么,在什么情况下不等于null
呢,在使用handler
的时候,还有一种使用方式及handler.post(Runnable r)
,看一下他的实现
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); // 赋值 m.callback = r; return m; }
这两个方法比较清晰,从这个地方可以看出,msg.callback
会调用我们的Runnable
对象。
而对于mCallback
对象,在构造方法中传入的,因为平常没有用过,都是使用无参的构造函数,基本上都为null。
那么,通过以上的分析,整个流程就比较清晰了:
ActivityThread.main()
中调用Looper.prepareMainLooper()
和Looper.loop()
方法。Looper.prepareMainLooper()
方法创建Looper
对象,同时Looper
对象中会保存与其对应的MessageQueue
对象。Looper.loop()
调用之后对MesageQueue
对象进行轮询。- 在创建
Handler
的时候,会保存当前线程的Looper
对象和消息队列MessageQueue
。 - 通过调用
handler.sendXXX()
将消息推送到消息队列。 - 消息队列中实际上调用的是
handler.handlerMessage()
方法,产生了联系。
子线程和主线程的使用区别
在实际使用中,子线程如果想要构造handler
对象,必须调用Looper.prepare()
和Looper.loop()
方法。而主线程不需要,为什么呢?
如果对于上面的源码分析理解清楚了可以清楚的知道,因为主线程中已经在ActivityThread.main()
即应用打开的初期初始化过Looper
对象并开始了消息队列。
- Android 从源码角度分析消息处理机制(Handler,Looper,Message)
- Android中关于Handler Message Looper 异步消息处理机制的最完整分析 从源码角度进行剖析
- 从Handler+Message+Looper源码带你分析Android系统的消息处理机制
- 从Handler+Message+Looper源码带你分析Android系统的消息处理机制
- Android:消息处理机制(Handler,Message,Looper)分析
- [Android实例] android的消息处理机制(图+源码分析)——Looper,Handler,Message
- 源码角度讲解Android消息处理机制(Handler、Looper、MessageQueue与Message)
- (转)android的消息处理机制(图+源码分析)——Looper,Handler,Message
- Android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- Android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- Android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- web开发模式+三层架构与MVC
- HTML 4.01的标准和规范以及如何使用中文
- python库
- Filter Initiated I/O
- 常见的编译错误
- Android 从源码角度分析消息处理机制(Handler,Looper,Message)
- 单链表-用尾插法创建单链表
- msysGit 安装后的配置
- VS2008 F5或Debuging Run 重新编译整个工程
- Codeforces Round #373 (Div. 2) E. Sasha and Array
- Sql基础练习03
- ubuntu下php5.6安装pcntl扩展
- 去哪儿网笔试题分析
- 编写saltstack 扩展模块