老生常谈了,我们所说Android的消息机制,其实就是Android中的Handle机制,主要用来解决的问题就是Android中不能在子线程中更新UI。其标志性的用法如下:
public class TestActivity extends Activity {
private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { //处理或查看消息 super.handleMessage(msg); System.out.println(msg.what); } }; @Override public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) { super.onCreate(savedInstanceState, persistentState); setContentView(R.layout.activity_main); new Thread(new Runnable() { @Override public void run() { //耗时操作 Message message = Message.obtain(); message.what = 1; mHandler.sendMessage(message); } }).start(); } }
上述代码大致是这样一个过程: 在主线程中创建Handle实例,并重写handleMessage方法用来接收和处理消息。另开启一个线程来进行比较耗时的操作,操作完成后将数据传递给Message对像,然后通过Handle来发送消息。Handle接收到消息之后,对消息进行分析并做出相应的处理,即更新UI。
延伸并十分值得注意的一点:Activity,Service都是运行在主线程中,所以在这两个组件中都不能进行耗时操作,否则会报ANR(应用无响应)的异常。一般的耗时操作需使用Android提供的AsyncTask,或者开启新的线程来处理。
下面对消息机制进行分析:
概述:Android的消息机制主要是指Handle的运行机制及Handle所关联的MessageQueue与loop的工作过程。
Handle分为四个部分:Handle,Message,MessageQueue,loop。下面详细介绍这四个部分,首先上图(消息机制的整体架构图)
Handle:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage)。包含Looper和MessageQueue,dispatchMessage()用来分发消息。发送消息的方式有两种,post()与sendMessage()。
Message:需要传递的消息,可以传递数据,并记录发送和处理消息的Handler。
MessageQueue:消息队列,其内部实现并不是用的队列,实际上是通过一个单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next)。主要是维护一组待处理的消息。
Loop:其中包含一个消息队列(MessageQueue),不断循环(Looper.loop),从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。
接下来说一下消息机制的运行流程:
在子线程执行完耗时操作,当Handler发送消息时,将会调用MessageQueue.enqueueMessage
,向消息队列中添加消息。当通过Looper.loop
开启循环后,会不断地从线程池中读取消息,即调用MessageQueue.next
,然后调用目标Handler(即发送该消息的Handler)的dispatchMessage
方法传递消息,然后返回到Handler所在线程,目标Handler收到消息,调用handleMessage
方法,接收消息,处理消息。
MessageQueue,Handler和Looper三者之间的关系:
每个线程中只能存在一个Looper,Looper是保存在ThreadLocal中的。主线程(UI线程,ActivityThread)已经创建了一个Looper,所以在主线程中不需要再创建Looper,但是在其他线程中需要创建Looper(looper.prepare())。每个线程中可以有多个Handler,即一个Looper可以处理来自多个Handler的消息。 Looper中维护一个MessageQueue,来维护消息队列,消息队列中的Message可以来自不同的Handler。在loop中,当next()取出下一条消息时,队列中已经没有消息时,next()会无限循环,产生阻塞,等待MessageQueue中加入消息,然后重新唤醒。
那我们如何获取到当前线程呢? Looper是保存在ThreadLocal中的,所以当然是从ThreadLocal中获取了。ThreadLocal是线程本地存储区(Thread Local Storage,简称为TLS),它并不是线程,而是用来在每个线程中保存数据,而且在不同的线程中数据互不干扰。这样,我们就能很方便的获取当前线程的loop对象了。
以上,就是Handle的知识点了。