(十四)Android的消息机制Handler

来源:互联网 发布:网络词表妹是什么梗 编辑:程序博客网 时间:2024/06/05 05:15
Android的消息机制主要指的是Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑
MessageQueue的中文翻译是消息队列,但其内部存储结构并非队列,而是单链表的数据结构来储存消息列表。
Looper可以理解为消息循环,MessageQueue只能存储消息,不能处理 ,而Looper则可以,它会以无限循环的形式去查询讯是否有新的消息。

在子线程中访问UI会报错,这个原因是因为在ViewRootImpl中的checkThread方法会对UI进行验证。
系统不允许在子线程中访问UI的原因:
          Android的UI控件不是线程安全的,若在多线程中访问,会导致UI控件处于不可预期状态。若加上锁,缺点有两个:1.加上锁会使UI访问逻辑变得复杂 2.锁机制降低访问效率,因为锁机制会阻碍某些线程的执行。

Handler创建时会采用当前线程的Looper来构建内部的消息循环系统,如果当前线程没Looper,就会报错。当创建完成后,会通过Handler的post方法将一个Runnable投递到Handler内部的Looper中去处理,也可以通过Handler的send方法发送消息,这个消息同样会在Looper中去处理。post方法其实也是调用send方法来完成的。当send方法被调用时,会调用MessageQueue的enqueueMessage方法来将这个消息放入消息队列中,然后Looper发现有新消息,就会处理。最终消息中的Runnable或者Handler的handleMessage方法就会被调用。Looper是运行在创建Handler所在的线程中的。

ThreadLocal的工作原理
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储后,只有在指定的线程中可以获取到存储的数据,对于其他线程来说则无法获取到。
ThreadLocal是一个泛型类,定义为:public class ThreadLocal<T>.
首先看其set方法,而在put方法中有一个规则,就是ThreadLocal的值在table数组中的存储位置总是为ThreadLocal的reference字段所标识的对象的下一个位置。


再看其get方法


从ThreadLocal的get和set方法中可以看出,他们所操作的对象都是当前线程的localValues对象的table数组,因此在不同线程中访问同一个ThreadLocal的get和set方法,他们对ThreadLocal所做的读/写操作仅限于各自线程内部。

消息队列的工作原理
         消息队列MessageQueue主要包含两个操作:插入和读取。读取操作本身会伴随着删除操作。插入和读取对应的方法分别为enqueueMessage和next。enqueueMessage 就是单链表的插入操作,next方法是一个无限循环的方法,若消息队列中没有消息,那么next方法会一直阻塞在这里,当有消息时,next方法会返回这条消息并将其从单链表中删除。
Looper的工作原理
     looper会不停的从MessageQueue那查看是否有消息,如有就立即处理,否则一直阻塞在那里。在Looper的构造方法中会创建一个MessageQueue即消息队列,然后将当前线程的对保存起来。
     looper的创建
     创建looper很简单,调用looper.prepare方法即可为当前线程创建一个Looper,接着通过Looper.loop方法开启消息循环。Looper还提供了一个prepareMainLooper方法给主线程(ActivityThread)创建Looper,其本质也是prepare方法。而getMainLooper方法则可获取到主线程的Looper。
     looper的退出
     Looper提供了quit和quitSafely来退出一个Looper,不同之处在于:quit是直接退出Looper,而quitSafaly是设置一个标记,然后将消息队列中的消息处理完毕之后安全退出。当Looper退出后,通过Handler发送的消息会失败,此时Handler的send方法会返回false。
     Looper最重要的一个方法是loop方法,looper是一个死循环方法,唯一跳出循环的条件是消息队列的next方法返回null,当Looper的quit方法被调用时,Looper会调用MessageQueue的quit或者quitSafely方法通知消息队列退出,当消息队列被标记为退出状态,他的next方法会返回null。也就是说Looper必须退出,否则就会无限循环下去。Looper通过调用MessageQueue的next方法获得消息,next是一个阻塞方法,无信息时一直阻塞,looper也一直阻塞。若有消息,则通过发送消息的Handler对象的dispatchMessage方法来处理消息,注:此dispatchMessage方法是在创建Handler时所使用的Looper中执行的,这样就将代码逻辑切换到指定线程中执行去了。





Handler的工作原理
     Handler的工作主要包含消息的接受和发送过程。消息的发送通过post和send方法。
     发送一条消息的过程调用的方法:sendMessage——>sendMessageDelayed——>sendMessageAtTime——>enqueueMessage
     将信息加入消息队列后,looper就开始处理消息了,最终由Looper交由Handler处理,即Handler的dispacthMessage方法会被调用,这样Handler就进入了消息处理阶段。

    Handler处理消息的过程为:



主线程的消息循环
Android的主线程就是ActivityThread,入口为main,在main方法中通过Looper.prepareMainLooper来创建主线程的Looper和MessageQueue,具体过程为:prepare(false)——>mThreadLocal.set(new Looper(false))——>创建MessageQueue;通过Looper.loop开启消息循环。
主线程消息开始循环以后,还需一个Handler来和消息队列进行交互,这个Handler是ActivityThread.H,它内部定义了一组消息类型,主要包含四大组件的启动和停止。
ActivityThread通过ApplicationThread和AMS进行进程间的通信,AMS以进程间通信的方式来完成ActivityThread的请求后会回调ApplicationThread中的Binder方法,然后ApplicationThread会向H发消息,H收到消息后将ApplicationThread中的逻辑切换到ActivityThread中去执行。这就是主线程的消息循环模型。

handler的sendMseeage方法最后调用的是enqueueMessage方法,将消息添加到消息队列。