Android消息机制
2016-04-27 16:25
309 查看
Android的消息机制主要是指Handler的运行方式。
对于Android开发者而言,Handler并不陌生,它常常被用来向主线程post消息,更新UI等操作。日常开发中只需和Handler类交互即可,然而,稍微深入了解Handler运行机制可以让开发者更加熟悉Android系统消息机制,做到运用自如。
Handler的运行需要MessageQueue和Looper的支撑。MessageQueue保存消息,提供消息队列的实现;Looper实现线程循环处理消息和分发;其他线程通过Handler对象可以向Looper线程发送消息(该消息最终由创建Handler的线程负责处理),Handler MessageQueue和Looper三者工作关系大致如下:
enqueueMessage方法原型如下:
next方法即是从链表中获取一条消息并返回给调用者,同时将该条消息从链表中删除;
MessageQueue的逻辑并不复杂,需要了解的基本操作就是插入和获取,很清晰。
Looper构建
Looper类核心变量:
其中sThreadLocal 是线程私有变量,为每个创建Looper的线程保存其创建的Looper对象;
mQueue是消息队列;
mThread是创建Looper的线程;
在Looper对象的构造方法中会初始化消息队列:
从上面代码片段可知Looper的构造函数是私有的,因此,Looper对象的构建并不是手动创建的。从源码可以得知,通过静态方法 prepare() 可以为当前线程创建Looper对象,同时在Looper的创建中又会创建消息队列。因此,在使用时只需在当前线程内调用Looper.prepare(),即可完成Looper对象和消息队列的构建,示例如下:
这样即可给线程”#SubThread”构建Looper和MessageQueue(当然仅仅是给Handler的使用创建必须的消息队列和循环机制,仅这样不能构建消息机制)。Looper还为主线程单独提供prepareMainLooper()方法(内部调用prepare方法),并保存主线程的Looper对象且提供获取主线程Looper的方法getMainLooper()。
开启消息循环
构建Looper之后必须调用loop()方法,手动开启消息循环。为线程构建消息队列的完整步骤如下:
分析源码可知,loop方法是一个死循环,不断地从消息队列中获取消息(如果队列暂时没有消息,则阻塞直至有线程向消息队列添加消息为止),并将消息分发。
至此 线程”#SubThread”已构建消息队列,并开启消息循环。其它线程通过handler即可向线程”#SubThread”发送消息。
可见,在主线程启动过程中,已经构建消息队列并开启消息循环,因此,在主线程中,我们可以方便的使用Handler,而无须关心其对应得Looper和MessageQueue。但是,为了更好理解消息机制,了解Handler是如何与Looper和MessageQueue协同工作还是有必要的。
Handler的创建
如上方式
代码逻辑很清晰,如果是在没有构建Looper的线程内调用此构造方法会抛出运行时异常,并提示“不可以在未调用Looper.prepare()方法的线程内创建Handler对象”;创建过程中handler会保存Looper中消息队列的引用,便于后续的消息添加。
通过Handler发送消息
通过调用Handler类提供的sendMessage系列方法可以发送消息,不同类型的sendMessage方法发送的消息内容设置稍有不同,以一条空消息为例,如下调用即可:
方法经过几次传递最终会调用enqueueMessage方法,该方法是Handler发送消息的实际操作部分,代码如下:
该方法其实也不是真正干活的,它调用MessageQueue的enqueueMessage方法向消息队列中添加一条消息。
因此通过Handler发送消息的逻辑最终归结为向MessageQueue消息链表内添加一条消息。
发送的消息如何传递至handleMessage()方法
一般在构建Handler对象时,我们会重写handleMessage方法,在该方法内处理各个不同类别的信息,典型逻辑如下:
那么经由handler 调用sendMessage的消息是如何被传递至handleMessage方法内的呢?
上一段,我们讲到sendMessage最终是在消息队列中添加一条消息记录,而是谁在不断读取并分发消息队列中的消息呢?没错就是Looper。Looper所在的线程调用loop()方法之后便在不断的从消息队列中读取消息并分发,其关键代码片段如下:
loop方法的逻辑也比较清晰:调用MessageQueue的next()方法获取一条消息,调用消息对象msg的target字段的dispatchMessage方法分发消息;
那么,分发消息应该就是由,这个msg所对应的target来处理了。
Message类中target字段定义
可知,target是一个Handler对象,而在sendMessage的过程中的最终函数enqueueMessage(代码见上文)中有关键的一句:
即将msg的target对象指向了发送msg的handler,因此,dispatchMessage最终还是由发送msg的Handler对象处理。
Handler的dispatchMessage方法如下:
终于,在这里调用了hanleMessage()方法,把消息处理的最终实体回调给Handler的定义者(hanleMessage()默认为空实现)。
看到这里,相信读者对Android的消息机制有一定的了解了。
那么有同学可能会问了,Handler内先通过sendMessage添加消息,再经由Looper回调handleMessage()方法,兜一圈不累吗?
Android系统的设计者们估计会说,累但很有必要。细心的读者会发现,调用sendMessage 和回调handleMessage的线程是不同的。sendMessage可以在任何子线程内通过handler对象发送,而回调handleMessage的线程是当初创建Handler ,Looper和MessageQueue的线程。这也就是为什么我们常常在主线程内创建Handler对象,重写相应的handleMessage()逻辑并且可以在此更新UI,而各子线程内一般只能通过这个Handler对象发送消息。
Handler,MessageQueue和Looper三者的配合构成了Android系统的消息机制,提供了一种线程通信实现方式,它在Android FrameWork 可以经常看到,使用简单(尤其是也主要是在UI线程和子线程通信的情况中),希望大家通过这篇文章可以对它有更好的了解。
对于Android开发者而言,Handler并不陌生,它常常被用来向主线程post消息,更新UI等操作。日常开发中只需和Handler类交互即可,然而,稍微深入了解Handler运行机制可以让开发者更加熟悉Android系统消息机制,做到运用自如。
Handler的运行需要MessageQueue和Looper的支撑。MessageQueue保存消息,提供消息队列的实现;Looper实现线程循环处理消息和分发;其他线程通过Handler对象可以向Looper线程发送消息(该消息最终由创建Handler的线程负责处理),Handler MessageQueue和Looper三者工作关系大致如下:
1.MessageQueue
该类是消息队列实现类,消息实体由Message类实现。在MessageQueue类中维护着一个Message链表(虽然取名为队列,其实现是单向链表),其核心操作是向消息队列添加消息以及获取消息,分别对应于方法enqueueMessage()和next()。enqueueMessage方法原型如下:
boolean enqueueMessage(Message msg, long when) { //单向链表同步插入 }
next方法即是从链表中获取一条消息并返回给调用者,同时将该条消息从链表中删除;
MessageQueue的逻辑并不复杂,需要了解的基本操作就是插入和获取,很清晰。
2.Looper
Looper类的核心职能是循环读取消息队列中的消息,并分发出去。Looper构建
Looper类核心变量:
// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); final MessageQueue mQueue; final Thread mThread;
其中sThreadLocal 是线程私有变量,为每个创建Looper的线程保存其创建的Looper对象;
mQueue是消息队列;
mThread是创建Looper的线程;
在Looper对象的构造方法中会初始化消息队列:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
从上面代码片段可知Looper的构造函数是私有的,因此,Looper对象的构建并不是手动创建的。从源码可以得知,通过静态方法 prepare() 可以为当前线程创建Looper对象,同时在Looper的创建中又会创建消息队列。因此,在使用时只需在当前线程内调用Looper.prepare(),即可完成Looper对象和消息队列的构建,示例如下:
new Thread("#SubThread"){ @Override public void run() { Looper.prepare(); } }.start();
这样即可给线程”#SubThread”构建Looper和MessageQueue(当然仅仅是给Handler的使用创建必须的消息队列和循环机制,仅这样不能构建消息机制)。Looper还为主线程单独提供prepareMainLooper()方法(内部调用prepare方法),并保存主线程的Looper对象且提供获取主线程Looper的方法getMainLooper()。
开启消息循环
构建Looper之后必须调用loop()方法,手动开启消息循环。为线程构建消息队列的完整步骤如下:
new Thread("#SubThread"){ @Override public void run() { Looper.prepare(); Handler handler = new Handler(); Looper.loop(); } }.start();
分析源码可知,loop方法是一个死循环,不断地从消息队列中获取消息(如果队列暂时没有消息,则阻塞直至有线程向消息队列添加消息为止),并将消息分发。
至此 线程”#SubThread”已构建消息队列,并开启消息循环。其它线程通过handler即可向线程”#SubThread”发送消息。
3.Handler
Handler类是消息机制中最常被开发者使用。常见的情况是在UI线程(主线程)中使用Handler,子线程通过Handler对象完成更新UI等操作,而无须接触MessageQueue和Looper。究其原因是主线程在启动过程中已完成MessageQueue和Looper的创建,以下是ActivityThread类main方法的代码片段:public static void main(String[] args) { //省略多行代码 Looper.prepareMainLooper(); //省略多行代码 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
可见,在主线程启动过程中,已经构建消息队列并开启消息循环,因此,在主线程中,我们可以方便的使用Handler,而无须关心其对应得Looper和MessageQueue。但是,为了更好理解消息机制,了解Handler是如何与Looper和MessageQueue协同工作还是有必要的。
Handler的创建
如上方式
Handler handler = new 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 that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
代码逻辑很清晰,如果是在没有构建Looper的线程内调用此构造方法会抛出运行时异常,并提示“不可以在未调用Looper.prepare()方法的线程内创建Handler对象”;创建过程中handler会保存Looper中消息队列的引用,便于后续的消息添加。
通过Handler发送消息
通过调用Handler类提供的sendMessage系列方法可以发送消息,不同类型的sendMessage方法发送的消息内容设置稍有不同,以一条空消息为例,如下调用即可:
handler.sendEmptyMessage(MSG_TEST);
方法经过几次传递最终会调用enqueueMessage方法,该方法是Handler发送消息的实际操作部分,代码如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
该方法其实也不是真正干活的,它调用MessageQueue的enqueueMessage方法向消息队列中添加一条消息。
因此通过Handler发送消息的逻辑最终归结为向MessageQueue消息链表内添加一条消息。
发送的消息如何传递至handleMessage()方法
一般在构建Handler对象时,我们会重写handleMessage方法,在该方法内处理各个不同类别的信息,典型逻辑如下:
Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case MSG_ONE: handleMsgOne(); break; case MSG_TWO: handleMsgTwo(); break; default: break; } } };
那么经由handler 调用sendMessage的消息是如何被传递至handleMessage方法内的呢?
上一段,我们讲到sendMessage最终是在消息队列中添加一条消息记录,而是谁在不断读取并分发消息队列中的消息呢?没错就是Looper。Looper所在的线程调用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; //省略多行 for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } //省略多行 msg.target.dispatchMessage(msg); //省略多行 msg.recycleUnchecked(); } }
loop方法的逻辑也比较清晰:调用MessageQueue的next()方法获取一条消息,调用消息对象msg的target字段的dispatchMessage方法分发消息;
那么,分发消息应该就是由,这个msg所对应的target来处理了。
Message类中target字段定义
/*package*/ Handler target;
可知,target是一个Handler对象,而在sendMessage的过程中的最终函数enqueueMessage(代码见上文)中有关键的一句:
msg.target = this;
即将msg的target对象指向了发送msg的handler,因此,dispatchMessage最终还是由发送msg的Handler对象处理。
Handler的dispatchMessage方法如下:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
终于,在这里调用了hanleMessage()方法,把消息处理的最终实体回调给Handler的定义者(hanleMessage()默认为空实现)。
看到这里,相信读者对Android的消息机制有一定的了解了。
那么有同学可能会问了,Handler内先通过sendMessage添加消息,再经由Looper回调handleMessage()方法,兜一圈不累吗?
Android系统的设计者们估计会说,累但很有必要。细心的读者会发现,调用sendMessage 和回调handleMessage的线程是不同的。sendMessage可以在任何子线程内通过handler对象发送,而回调handleMessage的线程是当初创建Handler ,Looper和MessageQueue的线程。这也就是为什么我们常常在主线程内创建Handler对象,重写相应的handleMessage()逻辑并且可以在此更新UI,而各子线程内一般只能通过这个Handler对象发送消息。
Handler,MessageQueue和Looper三者的配合构成了Android系统的消息机制,提供了一种线程通信实现方式,它在Android FrameWork 可以经常看到,使用简单(尤其是也主要是在UI线程和子线程通信的情况中),希望大家通过这篇文章可以对它有更好的了解。
相关文章推荐
- Android 插件换肤原理解析
- 解决Gradle DSL method not found: ‘android()’
- Android开源项目SlidingMenu深入剖析
- Service
- AndroidStudio常用快捷键
- 切换android SDK版本时出现的编译报错
- 【Android问题记录】Android在设置中切换语言后,回到Lanucher为什么会白屏
- Android 仿qq消息 可拖动回弹的泡泡
- Fragment
- Android笔记--Activity
- Android版本更新
- Android中Glide加载库的图片缓存配置究极指南
- Android反编译(jadx)
- Android 日常开发技术经验总结
- Android 日常开发技术经验总结
- Android 日常开发技术经验总结
- Android 日常开发技术经验总结
- Android 日常开发技术经验总结
- Android 日常开发技术经验总结
- Android内存优化