Android消息机制

来源:互联网 发布:大数据网络安全 编辑:程序博客网 时间:2024/05/22 19:26

Android消息机制Handler

提到Handler相信大家都并不陌生,在日常开发过程中不可避免地要涉及这方面的内容。从开发的角度来说,Handler是Android消息机制的上层接口,这使得开发过程中只需要和Handler交互即可。Handler使用过程中很简单,可以轻松将一个任务切换到Handler所在的线程中去执行。
Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑。MessageQueue的中文翻译是消息队列,以队列的形式对外提供插入和删除的工作。它内部采用是单链表的数据结构来存储消息列表。MessageQueue只是消息存储的单元并不能去处理消息。而Looper就填补了这个功能,Looper会以无限循环的形式去查找是否有新消息,如果有的话就处理消息,否则就一直等待着。Looper海油一个特殊的概念就是ThreadLocal,ThreadLocal并不是线程,它的作用是可以在每个线程中存储数据。我们知道Handler创建的时候会采用当前线程的Looper来构造消息循环系统,那么Handler内部如何获取到当前线程的Looper呢?这就要使用ThreadLocal了,ThreadLocal可以在不同的县城中互不干扰地存储并提供数据,通过ThreadLocal可以轻松获取每个线程的Looper。当然需要注意的是线程是默认没有Looper的,如果需要使用Handler就必须为线程创建Looper。我们经常提到主线程,也叫UI线程,它就是ActivityThread,ActivityThread被创建的时就会初始化Looper,这也是在主线程默认可以使用Handler的原因。

Handler的主要作用是将一个任务切换到某个指定的线程中去执行,这里描述下Handler的工作原理。Handler在创建的时候回采用当前线程的Looper来构建内部的消息循环系统,如果当前线程没有Looper,那么就会报错,can’t create handler 这个时候我们即可在当前线程创建Looper 记住创建Looper的话可以通过使用ThreadLocal 它可以任何线程中存储数据并提供数据。或者我们在一个有Looper的线程中创建Handler也行。
Handler创建完毕后,这个时候其内部的Looper以及MessageQueue就可以和Handler一起协同工作,然后通过Handler的post方法将一个Runnable投递到Handler内部的Looper中去处理,也可以通过Handler的send方法发送一个消息,这个消息同样会在Looper中去处理。其实Post方法最终也是送过send方法来完成,接下来主要来看一下send方法的工作过程。当Handler的send方法被调用时,它会调用MessageQueue的enqueueMessage方法将这个消息放入消息队列中,然后Looper发现有新消息到来时,就会处理这个消息,最终消息中的Runnable或者Handler的HandlerMessage方法就会被调用。注意Looper是运行在创建Handler所在县城中,这样一来Handler中的业务逻辑就被切换到创建Handler所在的线程中去执行了。

ThreadLocal的工作原理
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据,在日常开发中用到ThreadLocal的地方比较少,但是在某些特殊的场景下,通过ThreadLocal可以轻松地实现一些看起来很复杂的功能,这一点在Android的源码也所有体现,比如Looper、ActivityThrea以及AMS中都用到了ThreadLocal。具体到ThreadLocal的使用场景,这个不好统一来描述,一般来说,当某些数据是以线程作为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal。比如对于Handler来说,它需要获取当前的线程的Looper,很显然Looper的作用域就是线程并且不同的线程具有不同的Looper,这个时候通过ThreadLocal就可以轻松实现Looper在线程中的存取。如果不采用ThreadLocal,那么系统就必须提供一个全局的哈希表提供Handler查找指定线程的Looper,这样一来就必须提供一个类似于LooperManager的类了,但是系统并没有这么做而是选择了ThreadLocal,这就是ThreadLocal的好处。
ThreadLocal另一种使用场景是负责逻辑下的对象传递,比如监听器的传递,有些时候一个线程中的任务过于复杂,这可能表现为函数调用栈比较深以及代码入口的多样性,在这种情况下,我们又需要监听器能够贯穿整个线程的执行过程,这个时候可以怎么做呢?其实这个时候我们就可以采用ThreadLocal,采用ThreadLocal可以让监听器作为线程内的全局对象而存在,在线程内部只要通过get方法就可以获取到监听器。如果不采用ThreadLocal,那么我们能想到的可能是如下两种方法:第一种是将监听器通过参数的形式在函数调用栈中进行传递,第二种方法就是将监听器作为静态变量供线程访问。我们不管如何实现,我们要明白ThreadLocal内部最终实现的功能是,ThreadLocal可以在线程中存储数据,并且数据作用域是在该线程中的,在不同线程中的ThreadLocal是不同的。

MessageQueue消息队列的工作原理

消息队列在Android中指的是MessageQueue,MessageQueue主要包含两个操作:插入和读取。读取操作本身会伴随着删除操作,插入和读取对应的方法为enqueueMessage何next,其中enqueueMessage的作用是往消息队列中插入一条消息,而next的作用是从消息队列中取出一条消息并将其从消息队列中移除。而从这两个方法内部来看,enqueueMessage的实现来看,它的主要操作其实就是单链表的插入操作,而Next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里。当有新消息来时,next方法会返回这条消息并将从其单链表中移除。

Looper工作原理

looper在Android消息机制中扮演着消息循环的角色,具体来说它会不停的从MessageQueue中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在哪里。我们知道Handler工作原理需要Looper没有Looper就会报错,那么如何为一个线程创建Looper呢?我们通过Looper.prepare()即可为当前线程创建一个Looper,接着通过Looper.loop()来开启消息循环系统。Looper除了prepare()方法外,还提供了prepareMainLoopre方法,这个方法主要是给主线程也就是ActivityThread创建Looper使用的,其本质也是通过prepare方法来实现的。由于主线程的Looper比较特殊,所以Looper提供了一个getMainLooper方法,通过它可以在任何地方获取到主线程的Looper。Looper也是可以退出的,Looper提供了quit和quitSafely来退出一个标记,然后把消息队列中的已有消息处理完毕后才安全退出。Looper退出后,通过Handler发送的消息会失败,这个时候Handler的send方法会返回false。在子线程中,如果手动为其创建了looper完成事情后应该调用quit方法来终止循环,否则这个子线程就会一直处于等待的状态,而如果退出looper以后,这个线程就会立刻终止,因此建议不需要的时候终止looper。looper最重要的一个方法是loop只有调用了该方法,消息循环系统才会真正的起作用。loop方法也比较好理解,唯一跳出循环的方式是MesageQueue的next方法返回了null。当looper的quit方法被调用时,looper就会调用MessageQueue的quit或者quitSafely方法来通知消息队列退出,当消息队列被标记退出状态时,它的next方法就会返回null。也就是说,Looper必须退出,否则loop方法就会无限循环下去。loop方法会调用messagequeue的next方法来获取新消息,而next是一个阻塞操作,当没有消息时,next方法会一直阻塞在哪里,这也导致了loop方法一直阻塞在哪里。如果messagequeue的next方法返回了新消息,looper就会处理这条信息msg.target.dispatchMessage(msg),这里的msg.target是发送这条消息的handler对象,这样handler发送的消息最终又交给它的dispatchMessage方法来处理了。但是这里不同的是,Handler的dispatchMessage方法是在创建handler时所用的looper中执行的,这样就成功地将代码逻辑切换到指定的线程中去执行了。

Handler工作原理
handler的工作主要包含消息的发送和接收过程。消息的发送可以通过post的一系列方法以及send的一系列方法来实现,post的一系列方法最终是通过send的一系列方法来实现的。handler发送消息的过程中仅仅是向消息队列中插入了一条消息,MessageQueue的next方法就会返回这条消息给looper,looper收到消息后就开始处理了,最终消息由looper交友给handler处理,即handler的dispatchMessage方法会被调用,这时handler就进入了处理消息的阶段。最终会被handlerMessage方法来处理消息。