【Android 消息机制】Handler
文章目录1. 基本原理2. 核心对象2.1 Handler2.1.1 常用方法2.1.2 小结2.2 Looper2.2.1 概述2.2.2 源码分析(1) prepare(2) loop()(3) quit()/quitSafely()(4) Looper.myLooper()(5) 构造方法2.3 MessageQueue2.3.1 概述2.3.2 源码分析(1) enqueueMessage(2) 读取消息的next方法2.4 Message2.4.1 概述2.4.2 源码分析(1)重要属性(2) obtain方法(3) recyler3. 其他问题3.1 HandlerThread3.2 IdleHandler3.3 主线程Handler的流程图4. 思维导图- 参考资料添加链接描述添加链接描述添加链接描述1. 基本原理Handler机制是Android中基于单线消息队列模式的一套线程消息机制。Handler的设计是基于生产者和消费者模型的方式工作流程如下handler将消息发送到Looper的消息队列中messageQueuemessageQueue 将数据按照时间先后排好队等待Looper.loop()按照先后顺序取出MessageLooper.loop()取出消息之后调用消息的Message的target即附属的Handler的dispatchMessage方法将该消息回调到handleMessage方法中.handler 在 handleMessage(msg)方法中处理我们自己的逻辑。2. 核心对象2.1 Handler添加链接描述2.1.1 常用方法2.1.2 小结核心方法主线程和子线程创建handler的区别基本工作原理(与其他三个组件的联动关系)内存泄漏问题数量问题2.2 Looper添加链接描述2.2.1 概述2.2.2 源码分析(1) prepareLooper 采用静态变量 sThreadLocal:ThreadLocal 管理着与线程相关的 Looper 实例当重复调用 Looper.prepare() 方法时如果 sThreadLocal:ThreadLocal.get() 不为空则会抛异常。由此保证了一个线程 Thread 有且只有一个与之关联的 Looper 对象(2) loop()publicstaticvoidloop(){finalLoopermemyLooper();if(menull){thrownewRuntimeException(No Looper; Looper.prepare() wasnt called on this thread.);}finalMessageQueuequeueme.mQueue;// 无限循环for(;;){Messagemsgqueue.next();// 可能会阻塞if(msgnull){// No message indicates that the message queue is quitting.return;}// 分发消息前的钩子me.mLogging.println( Dispatching to msg.target msg.callback);// 分发消息给对应的 Handlermsg.target.dispatchMessage(msg);// 分发消息后的钩子me.mLogging.println( Finished to msg.target msg.callback);// 回收消息对象msg.recycleUnchecked();}}在一个无限循环中不断重复以下操作① 调用 MessageQueue.next() 获取下一个消息该方法会阻塞如果返回 null则退出循环。② 当有返回消息且不为空时调用 Message.target.dispatchMessage() 方法分发消息。③ 把分发后的消息对象 Message回收到消息池中以便复用。进入下一个循环。(3) quit()/quitSafely()退出消息循环终止 loop()。Looper 被退出后任何向队列发布消息的尝试都将失败。quit()直接移除消息队列中所有消息使得 MessageQueue.next() 因为没有消息而返回null致使Looper.loop() 退出循环。quitSafely()只会剔除执行时刻 when 晚于当前调用时刻的 Message 消息。这样可以保证 quitSafely() 调用的时刻满足执行时间条件的 Message 会继续保留在队列中被执行并在所有可执行的消息都执行完毕之后才退出 loop() 的轮询。由上可知方法内部是通过调用消息队列 MessageQueue.quit() 方法来实现的。(4) Looper.myLooper()返回与当前线程关联的 Looper 对象。 如果调用线程未与 Looper 关联则返回 null。(5) 构造方法quitAllowed表示该 Looper 是否允许退出即是否允许调用 quit()privateLooper(booleanquitAllowed){mQueuenewMessageQueue(quitAllowed);mThreadThread.currentThread();}2.3 MessageQueue添加链接描述2.3.1 概述2.3.2 源码分析(1) enqueueMessagebooleanenqueueMessage(Messagemsg,longwhen){// target是handler对象检查消息是否有线程处理if(msg.targetnull){thrownewIllegalArgumentException(Message must have a target.);}// msg是否已经被使用if(msg.isInUse()){thrownewIllegalStateException(msg This message is already in use.);}synchronized(this){// 处理消息的线程是否已经退出如果线程退出则将消息回收掉无需进队列if(mQuitting){IllegalStateExceptionenewIllegalStateException(msg.target sending message to a Handler on a dead thread);Log.w(TAG,e.getMessage(),e);msg.recycle();returnfalse;}// 标记消息已经被使用msg.markInUse();msg.whenwhen;// 队头MessagepmMessages;booleanneedWake;if(pnull||when0||whenp.when){// 将消息放入队头位置// New head, wake up the event queue if blocked.msg.nextp;mMessagesmsg;needWakemBlocked;}else{// 将新消息插入到队列中同步消息根据when来确定队列位置如果是同步屏障同步屏障消息targetnull且新消息是异步的则needWake为true则计划唤醒队列。// Inserted within the middle of the queue. Usually we dont have to wake// up the event queue unless there is a barrier at the head of the queue// and the message is the earliest asynchronous message in the queue.needWakemBlockedp.targetnullmsg.isAsynchronous();Messageprev;for(;;){prevp;pp.next;if(pnull||whenp.when){break;}// 如果队头的一下个消息也是异步消息且未到唤醒时间则将needWake置为false不唤醒队列if(needWakep.isAsynchronous()){needWakefalse;}}msg.nextp;// invariant: p prev.nextprev.nextmsg;}// 是否需要唤醒队列// We can assume mPtr ! 0 because mQuitting is false.if(needWake){nativeWake(mPtr);}}returntrue;}首先是对消息进行检验消息能否被处理和消息是否正在被使用等。msg.target是指Handler对象。当处理消息的线程已经退出则消息会被回收。针对同步消息:将时间小的消息放在靠近队头的位置时间长的消息靠近队尾。从队头开始找到新消息在队列中合适的位置然后将新消息插入到队列中如果队列为null或新消息的等待时间小于队头消息的等待时间则直接将新消息放入到队列的头部。如果队头消息的时间小于新消息则新消息将从队头消息开始依次和队列中的消息进行比较直到找到合适的位置。这里特别说明下同步消息和异步消息只有isAsynchronous()可以区分即异步标志位区分异步标志位是通过msg的setAsynchronous(boolean async)方法进行设置异步和同步消息均是根据when来确定在队列中的位置。(2) 读取消息的next方法Messagenext(){// 只有当线程退出时mPtr为null// Return here if the message loop has already quit and been disposed.// This can happen if the application tries to restart a looper after quit// which is not supported.finallongptrmPtr;if(ptr0){returnnull;}intpendingIdleHandlerCount-1;// -1 only during first iterationintnextPollTimeoutMillis0;for(;;){if(nextPollTimeoutMillis!0){Binder.flushPendingCommands();}nativePollOnce(ptr,nextPollTimeoutMillis);synchronized(this){// Try to retrieve the next message. Return if found.finallongnowSystemClock.uptimeMillis();MessageprevMsgnull;MessagemsgmMessages;// 同步屏障优先处理异步消息此时同步消息不会被处理体现出消息的优先级可以优先处理高优先级的消息即异步消息。if(msg!nullmsg.targetnull){// Stalled by a barrier. Find the next asynchronous message in the queue.do{prevMsgmsg;msgmsg.next;}while(msg!null!msg.isAsynchronous());}if(msg!null){// 消息未到触发时间设置新的唤醒时间if(nowmsg.when){// Next message is not ready. Set a timeout to wake up when it is ready.nextPollTimeoutMillis(int)Math.min(msg.when-now,Integer.MAX_VALUE);}else{// 获取到消息同时重组新的队列取走的消息从队列中移除// Got a message.mBlockedfalse;if(prevMsg!null){prevMsg.nextmsg.next;}else{mMessagesmsg.next;}msg.nextnull;if(DEBUG)Log.v(TAG,Returning message: msg);msg.markInUse();returnmsg;}}else{// No more messages.nextPollTimeoutMillis-1;}// Process the quit message now that all pending messages have been handled.if(mQuitting){dispose();returnnull;}...}}}在next方法中当发现队列中有同步屏障时此时会优先返回队列中的异步消息从队列中获取异步消息并将该异步消息从队列中移除如果同步屏障不被移除即使异步消息被处理完毕同步消息也不会被处理队列会进入阻塞状态。如果队列中没有同步屏障则从队列中获取同步消息并将该同步消息从队列中移除。2.4 Message2.4.1 概述消息类型如何创建Message2.4.2 源码分析(1)重要属性比如target属性对于屏障消息这里target就是nullwhen属性handler.postDelay方法底层利用了when属性// 用户自定义主要用于辨别Message的类型publicintwhat;// 用于存储一些整型数据publicintarg1;publicintarg2;// 可放入一个可序列化对象publicObjectobj;// Bundle数据Bundledata;// Message处理的时间。相对于1970.1.1而言的时间// 对用户不可见publiclongwhen;// 处理这个Message的Handler// 对用户不可见Handlertarget;// 当我们使用Handler的post方法时候就是把runnable对象封装成Message// 对用户不可见Runnablecallback;// MessageQueue是一个链表next表示下一个// 对用户不可见Messagenext;(2) obtain方法publicstaticMessageobtain(){synchronized(sPoolSync){if(sPool!null){MessagemsPool;sPoolm.next;m.nextnull;m.flags0;// clear in-use flagsPoolSize--;returnm;}}returnnewMessage();}(3) recylerpublicvoidrecycle(){if(isInUse()){if(gCheckRecycle){thrownewIllegalStateException(This message cannot be recycled because it is still in use.);}return;}recycleUnchecked();}voidrecycleUnchecked(){flagsFLAG_IN_USE;what0;arg10;arg20;objnull;replyTonull;sendingUidUID_NONE;workSourceUidUID_NONE;when0;targetnull;callbacknull;datanull;synchronized(sPoolSync){if(sPoolSizeMAX_POOL_SIZE){nextsPool;sPoolthis;sPoolSize;}}}Message的作用就是承载消息他的内部有很多的属性用于给用户赋值。同时Message本身也是一个链表结构无论是在MessageQueue还是在Message内部的回收机制都是使用这个结构来形成链表。同时官方建议不要直接初始化Message而是通过Message.obtain()方法来获取一个Message循环利用。一般来说我们不需要去调用recycle进行回收在Looper中会自动把Message进行回收3. 其他问题3.1 HandlerThread普通线程一次性执行 → 跑完销毁HandlerThread常驻子线程接收消息串行执行任务3.2 IdleHandler3.3 主线程Handler的流程图4. 思维导图概述通过网盘分享的文件消息机制Handler.xmind链接: https://pan.baidu.com/s/1kEuvqI4LFb–Y3HBkXLnaQ 提取码: wce4 复制这段内容后打开百度网盘手机App操作更方便哦源码思维导图