Handler
Android的消息机制主要指的是Hanlder的运行机制、Handler所附带的MessageQueue和Looper的工作过程,三者是一个整体的,只不过我们开发过程中接触比较多的是Hanlder而已。
Handler创建完成后,此时内部的Looper和MessageQueue就可以和Handler协同工作了,然后通过Handler的post发送一个Runable投递到Hanlder内部的Looper处理,也可以通过send发送一个message,这消息同样会在Looper处理,其实post方法最终也是通过调用send方法来完成的。
send方法被调动是会调用MessageQueue的enqueueMessage方法将这个消息放到消息队列中,然后Looper发现有新消息,就会处理这个消息。最终消息中的Runable、或者Handler的handlerMessage方法就会被调用。Looper是运行在创建Hanlder的线程中、或者执行线程的Looper,这样一来Handler中的业务逻辑就被切换到Handler所在的线程中执行。
Handler
:相当于一个处理器,主要处理和绑定该handler线程中的message。每个handler都必须关联一个looper,并且二者是一一对应的。Handler负责消息的发送和处理:Handler发送消息给MessageQueue和接收Looper返回的消息并且处理消息。
MessageQueue
:消息队列,顾名思义,内部存储一组消息,以队列的形式对外提供插入、删除工作。内部从存储结构不是真正的队列,而是采用单链表的数据结构存储消息,单链表在插入和删除上比较有优势。
enqueueMessage()
:插入方法next()
:读取方法,伴随着删除。next是一个无限循环方法,如果消息队列中没有消息,就一直阻塞,有新消息到来是,next会返回这条消息并从单链表中移除。quit()、quitSafely()
:退出、安全退出方法。如果Looper.quit或者Looper.quitSafely被调用,就调用MessageQueue的quit、quitSafely通知消息队列退出,消息队列被标记为退出是,next就返回null==
Looper
:中文翻译为循环,理解为消息循环。由于MessageQueue只是消息的存储单元,不去处理消息,而Looper就填补了这个功能。Looper会以无线循环的形式查看是否有新的消息,有的话就负责从内部的messageQueue中拿出一个message交给handler进行处理。这里handler是在UI线程中实现的,所以经过这么一个handler、 message机制,我们就可以回到UI线程中了。
Looper(boolean quitAllowed)
:构造方法,是私有的Looper.prepare()
:为当前线程创建一个Looper,内部调用私有构造方法Looper.loop()
:开启消息循环,是一个死循环,只有MessageQueue.next返回null才推出。Looper.prepareMainLooper()
:给主线程ActivityThread创建Looper,由于主线程Looper方法比较特殊,所以提供一个单独方法Looper.getMainLooper()
:在任何地方获取主线程的LooperLooper.quit()
:直接退出Looper。Loop.quitSafely()
:只是设定一个退出标记,当把队列所以的消息处理完毕才安全退出。Looper退出后,Handler的send发送的消息会失败,测试send方法返回FALSE。
Looper.quit()–>MessageQueue.quit,测试MessageQueue.next 返回null,Looper.loop退出死循环、也就是消息循环
ThreadLocal<T>
:java中的一个特殊概念,传入泛型,并不是线程,作用是可以在每个线程中存储数据,作用域是当前线程,实现多个线程互不干扰的存取和修改数据。Hanlder创建的时候会采用当前线程的Looper来构造消息循环系统,那么Hanlder内部获取当前线程的Looper,就要使用到ThreadLocal了。ThreadLocal可以在不同线程中互不干扰的的存储并提供数据,通过ThreadLocal可以轻松的获取到每个线程的Looper。当然需要注意的是,线程是默认没有Looper的,如果需要使用Hanlder就必须为该线程创建Looper。我们经常用的主线程,也叫UI线程,其实是ActivityThread,在创建时候就被初始化Looper了,这也是主线程总默认可以使用Handler的原因
// Looper中的代码
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
[x] 调用Looper类的 prepare() 方法可以为当前线程创建一个消息循环,调用loop() 方法使之处理信息,直到循环结束。
[x] 我们要在子线程中调用Looper.prepare() 为一个线程开启一个消息循环,默认情况下Android中新诞生的线程是没有开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环。) Looper对象通过MessageQueue来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。 然后通过Looper.loop() 让Looper开始工作,从消息队列里取消息,处理消息。
[x] 注意:写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。
[x] Handler有几个构造重载,如果构造时不提供Looper类对象参数,会获取当前线程的Looper对象,即将当前线程的消息循环作为Handler关联的消息循环。
[x] 其实Looper的作用就是把handler发送的消息放 到Queue中,并把消息广播给所有与这个Queue相关的handler,而Queue一般是主线程开启的时候就给这个线程分配了一个,所以你要与UI 主线程通信必须用于这个Queue相关联的handler对象才行,一般handler对象在那个线程中创建的就与那个线程的queue关联,所以在UI 线程中创建的handler对象就与UI线程通讯,这样我们就可以在子线程中发送消息给主线程,实现更新UI的功能。
//uiHandler在主线程中创建,所以自动绑定主线程,不需要放入onCreate中
private