Android进阶 - 消息处理机制探索
前言
Android消息机制,是Android中核心机制之一,进阶路上的基础知识,其主要指的是Handler运行机制,而Handler运行需要底层的MessageQueue、Looper的支撑,下面我们共同探索。
核心概念综述
Handler
对于Handler,许多初学者认为它基本用于更新UI。
而官方解析道:
There are two main uses for a Handler:
(1) to schedule messages and runnables to be executed as some point in the future;
(2) to enqueue an action to be performed on a different thread than your own.
翻译过来:
** (1) 调度message、runnable的执行
(2) 使动作在不同的线程中执行。**
其并没有提及更新UI的作用,从本质上看,Handler并不是专门用来更新UI的,只是对于开发者来说,它常被用于更新UI,实际Handler是Android消息机制的一个重要组成部分,更新UI不过是消息传递与处理的其中一部分。
Message
代表一个行为或是一串动作,每一个消息加入消息队列时,都有明确的Handler作为目标。
MessageQueue
字面意思 - 消息队列,以队列形式对外提供插入、删除工作,但内部以单链表存储结构实现,并不是真正的队列,而是采用单链表的数据结构来存储消息列表。
Looper
消息循环,Looper会以无限循环形式去MessageQueue查找是否有新消息,如果有则处理,没有则等待。
注意:
- 线程中默认没有Looper,如果使用Handler就必须为线程创建Looper。创建Handler之前,需要Looper.prepare(),最后要补上Looper.loop();
- 主线程(UI线程),ActivityThread创建时已经初始化Looper了,故可以直接使用
ThreadLocal
Handler创建时候需要获取到当前线程中的Looper来构造消息循环系统,Handler内部正是通过ThreadLocal来获取的。
ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。
详解可参见:解密ThreadLocal
消息机制流程解析
总体流程
创建 Handler时,会获取当前线程的Looper来构建消息循环系统,如果当前线程没有Looper,则会报错。
//Handler无参构造方法(部分)public Handler() { ... //从sThreadLocal中取出当前对象的Looper mLooper = Looper.myLooper(); //检查Looper是否为空 if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = null;}
Handler创建完毕后,内部的Looper以及MessageQueue就可以与Handler一起协同工作了,Handler通过post投递Runnable或者send方法发送消息(post方法最终也是通过send方法来完成的),Runnable与Message最终都会交给Looper中处理。
Send方法流程:通过MessageQueue的enqueueMessage方法将消息放入消息队列,Looper发现有新消息到来,则处理该消息。最终消息中的Runnable或者HandlerMessage方法会被调用。
**注意: **
- Looper运行在Handler所在线程中的,当消息交给Looper处理时候,业务逻辑已经成功切换到创建Handler所在的线程中了。
- 每个线程都至多只可以有一个Looper对象,也只可以有一个与该Looper对象关联的MessageQueue对象。
- 每个线程可以有多个Handler对象,Looper将Message取出来后,会调用与该Message关联的Handler的dispatchMessage方法。
下面引用一张关系来阐述:
**解析: **
- Runnable和Message可以被压入某个MessageQueue。而需要注意的是,某种类型的MessageQueue一般只允许保存相同类型的Object
- 实际上,源码中会对Runnable进行相应处理,转换成Message再放入到对应MessageQueue中
流程总结:
- Handler创建时,通过sThreadLocal取出当前对象的Looper构建消息循环系统,若当前线程没有Looper则报错
- Handler通过post或send方法将Runnable投递或者Message发送,Message入队到MessageQueue,同时Looper死循环查看MessageQueue是否有消息,不断取出新消息,交给对应Handler处理。(取出消息的时,内部实现了跨线程通信)
- 即:Looper不断获取MessageQueue中的一个Message,然后由Handler来处理
深入理解 - 源码探究
消息机制相关类联系
- Thread - Looper 1对1,一个Thread最多只能拥有一个Looper
- Looper - MessageQueue 1对1,一个Looper只能对应一个MessageQueue
- MessageQueue - Message 1对N,一个MessageQueue中有N个Message
- Message - Handler N对1,每个Message最多指定一个Handler
- Thread - Handler 1对N,由上述关系推断
相关类的类关系图:
Handler
源码路径:frameworks/base/core/java/android/os/Handler.java
Handler功能一 - 处理Message(本职工作)
//相关函数public void dispatchMessage(Message msg);//对Message进行分发public void handleMessage(Message msg);//对Message进行处理//源码-----------------------------------------------------------public void dispatchMessage(Message msg) { //判断消息是否携带callback(Runnable对象) if (msg.callback != null) { //消息携带callback,则直接执行callback handleCallback(msg);//方法实质:msg.callback.run(); } else { //判断Handler.mCallback(Runnable对象)是否为空 if (mCallback != null) { //使用自带的mCallback处理 if (mCallback.handleMessage(msg)) { return; } } //消息没有携带callback,handler自身也没有mCallback才调用 handleMessage(msg); }}/** * Subclasses must implement this to receive messages. * 子类必须实现该方法去接收msg */public void handleMessage(Message msg) {}
- Looper从MessageQueue中取出一个Message后,首先会调用dispatchMessage方法进行消息派发
- dispatchMessage方法会根据具体策略来将Message分发给相应负责类处理(上述源码中为默认处理方式)
- Handler的扩展子类可以重写上述两个方法来改变它的默认行为
Handler功能二 - 将Message压入MessageQueue
注意:该功能的设计形成了一个神奇的“循环圈”,熟悉类关系图(见上文),便很好理解了:
Handler -(压入消息)-> MessageQueue -(Looper取出消息)-> Message -(传递消息给Handler处理)-> Handler
//相关函数(部分)//Post:public final boolean post(Runnable r)public final boolean postAtTime(Runnable r, long uptimeMillis)...//Send:public final boolean sendMessage(Message msg)public final boolean sendEmptyMessage(int what)public final boolean sendEmptyMessageDelayed(int what, long delayMillis)public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)...//源码(部分)-------------------------------------------------------public final boolean post(Runnable r) { //Post内部调用Send,需要先把Runnable转成Message return sendMessageDelayed(getPostMessage(r), 0); }/** * 将Runnable转成Message的主要方法 */private static Message getPostMessage(Runnable r) { /*Android系统内部维护一个全局Message池,当用户需要用Message时, 通过Message.obtain直接获取即可,避免创建,节约资源*/ Message m = Message.obtain(); //转换实质:将Runnable对象设置成Message的callback(回调函数) m.callback = r; return m; }/** * 准备好Msg,发送消息,实现delay逻辑后,调用sendMsgAtTime */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; //一般每个Thread都会有一个MessageQueue来承载消息 if (queue == null) { //没有Queue则抛异常 RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } //压入MQ return enqueueMessage(queue, msg, uptimeMillis);}
MessageQueue
源码:frameworks/base/core/java/android/os/MessageQueue.java
//构造方法MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; //本地创建一个NativeMessageQueue对象并赋值给mPtr(long类型) mPtr = nativeInit();}//本地方法(C++实现)static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) { //创建NativeMessageQueue对象 NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); if (!nativeMessageQueue) { jniThrowRuntimeException(env, "Unable to allocate native queue"); return 0; } nativeMessageQueue->incStrong(env); //返回地址 return reinterpret_cast(nativeMessageQueue);}//对应的销毁本地方法static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr); nativeMessageQueue->decStrong(env);}//主要方法声明boolean enqueueMessage(Message msg, long when)//入队Message next()//出队void removeMessages(Handler h, int what, Object object)//删除元素void removeMessages(Handler h, Runnable r, Object object)
Looper
源码路径:frameworks/base/core/java/android/os/Looper.java
Android源码角度上看,消息机制设计思想更贴近下图:
解析:从上文类关系图可知,Looper含有mQueue、mThread,Handler含有mQueue、mLooper,MessageQueue含有mMessages。这样,图就简化成Looper与Handler(其实还有Thread)之间的关系了。
Looper在普通线程中的工作过程:
//举个一般的使用栗子class TestThread extends Thread { public Handler mHandler; @Override public void run() { Looper.prepare(); mHandler = new Handler() { @Override public void handleMessage(Message msg) { //通常需要重写该方法来处理消息 } }; Looper.loop(); } }
解析:
上述代码关键有三个步骤:
- Looper.prepare(); - Looper的准备工作
- 创建处理消息的Handler
- Looper.loop(); - Looper的开始运作
步骤一 - Looper.prepare()
static final ThreadLocal sThreadLocal = new ThreadLocal(); /** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */public static void prepare() { prepare(true);}//prepare实质工作就是private static void prepare(boolean quitAllowed) { //保证Looper在某个Thread的唯一存在 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } //将Looper保存到sThreadLocal //(ThreadLocal下面再详细阐述,这里只要知道:其主要作用是为每个Thread互不干扰保存独有的数据即可) sThreadLocal.set(new Looper(quitAllowed));}//构造方法,构建MQ、获取当前Threadprivate Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread();}
步骤二 - 创建处理消息的Handler
//Handler重要成员变量,具有多个初始化以下成员变量的构造方法final MessageQueue mQueue;final Looper mLooper;final Callback mCallback;//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()); } } //本质通过sThreadLocal.get()得到 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } //从Looper中得到,mQueue担当Looper与Handler之间沟通的桥梁 mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async;}
步骤三 - Looper.loop()
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */public static void loop() { //获取当前Looper(即sThreadLocal.get()获得的Looper) final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } //获取Looper中的MQ 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. //确保当前线程的id与本地进程相一致,保持追踪实际id(翻译自官方文档,非本文讨论重点,可忽略) Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { //开启消息死循环 Message msg = queue.next(); // 取出消息,可能会阻塞 if (msg == null) { //没有消息则退出循环 return; } ... msg.target.dispatchMessage(msg);//调用Handler中的dispatchMessage分发消息,target本质为Handler if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. //确保id在分发消息过程中不发生错误变化(翻译自官方文档,非本文讨论重点,可忽略) final long newIdent = Binder.clearCallingIdentity(); ... msg.recycleUnchecked();//处理完毕,进行回收 }}
Looper总结:不断从消息队列中取出消息,再分发给对应的Handler,如果消息为空,则跳出死循环,进入睡眠以让出CPU资源。
拓展:具体事件处理过程中,程序会post新的事件进入队列,其他进程也可能投递新的事件到这个队列中,APK应用程序本质上就是不停地执行“处理队列事件”的工作,直至它退出。
ThreadLocal
ThreadLocal是线程内部的数据存储类,各个线程相互隔离、相互独立。
主要作用:
- 实现Looper在线程中的存取,使得线程与Looper能够一一对应
- 实现负责逻辑下对象的传递,例如:让监听器作为线程内的全局对象,通过ThreadLocal,线程内部只要通过get即可获取到,简单又强势(当然也可以有其他方法解决:1、以函数参数方式传递;2、以监听器作为静态变量供线程访问;两种方法都有其局限性,综合来看,采用ThreadLocal是最好解决方法)
源码分析:
//为ThreadLocal设置数据public void set(T value) { //得到当前Thread Thread currentThread = Thread.currentThread(); //得到当前Thead中的数据,如果为空则先初始化 Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value);}//具体存储过程,可不必深究算法//简单来说:ThreadLocal中有一个table数组,具体的值存储在reference字段所标识的下一个位置//例如:ThreadLocal的reference对象在table中索引为index,那么其值就存在数组的index+1的位置void put(ThreadLocal<?> key, Object value) { ... for (int index = key.hash & mask;; index = next(index)) { Object k = table[index]; if (k == key.reference) { // Replace existing entry. table[index + 1] = value; return; } if (k == null) { if (firstTombstone == -1) { // Fill in null slot. table[index] = key.reference; table[index + 1] = value; size++; return; } // Go back and replace first tombstone. table[firstTombstone] = key.reference; table[firstTombstone + 1] = value; tombstones--; size++; return; } // Remember first tombstone. if (firstTombstone == -1 && k == TOMBSTONE) { firstTombstone = index; } }}//取出ThreadLocal值,如果值为空则返回初始值(null)public T get() { // Optimized for the fast path. 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); }
学习资源推荐
Handler Looper MessageQueue 详解
Java并发包学习 - 解密ThreadLocal
Android消息处理机制(Handler、Looper、MessageQueue与Message)
Android消息机制 -
Android异步消息处理机制完全解析,带你从源码的角度彻底理解 - 郭霖
Android应用程序消息处理机制分析 - 老罗
Android应用程序线程消息循环模型分析 - 老罗
更多相关文章
- 第八章、理解Window和WindowManager
- Android文本输入框EditText方法说明和属性
- Java集合 && Android提供的集合
- Unable to execute dex: java.nio.BufferOverflowException解决
- Android中OptionMenu用法实例
- AndroidStudio3.2 Failed to resolve:.......问题解决方法
- Android使用criteria选择合适的地理位置服务实现方法
- android 升级webview的方法
- 实例演示Android异步加载图片