消息模型
一、UI线程
什么是UI线程?
Android应用在启动时间会首先创建一个主线程(main thread),它是应用程序的入口,主要负责管理UI以及分发事件,习惯上称之为UI线程(UI thread)。
1、 不是线程安全的,对UI的操作操作必须在UI线程中进行,否则系统将抛出异常;
ProgressBar通过setProgress更新UI ?
在更新进度的时候会判断当前线程是否为UI线程,是UI线程直接调用刷新方法,不是的话就调用view的post方法,将runable放到UI线程的消息队列等待处理,这个就涉及到Handler,下文再讲。
Android提供了以下一些方法,从其他线程访问UI线程:
· Activity.runOnUiThread(Runnable)
· View.post(Runnable)
· View.postDelayed(Runnable,long)
2、UI线程不能被阻塞,阻塞时间超过5s,出现ANR,影响用户体验,对于耗时的操作一定要在非UI线程中执行。
二、消息系统与消息模型
Android实现自己的消息系统,抽象出Message、MessageQueue、Looper、Handler等概念,这些组件巧妙结合形成了Android的消息模型,首先我们了解一下消息系统的构成要素和基本原理。
2.1消息系统构成要素和基本原理
从一般的系统设计层次来说,基本的消息循环系统需要包含以下几个要素。
●消息队列
●发送消息
●消息读取
●消息分发
●消息循环线程
消息系统必须要依赖一个消息循环来轮询自己的消息队列,如果有消息进来,就调用消息处理函数,根据消息类型及其参数做相应的处理。消息系统要运作起来,必定有消息的产生和消费。暂且把产生消息的线程称作生产者线程,把消费者线程称作消费者线程。生产者线程将消息发送到消息队列,消费者线程从消息队列中取出消息进行相应的处理。当消息队列中没有消息时,消费者线程便进入了挂起状态,而当有新的消息到达时,消费者线程会被唤醒继续运行。当然生产者也可以是消费者。
图2.1 基本的消息循环模型
2.2消息模型基本原理
Android的消息系统使用了Message、MessageQueue、Looper、Handler等概念,从消息系统的基本原理可以知道这些都是概念包装,本质的东西是消息队列中消息的分发处理方法。Android巧妙地利用了对象抽象技术抽象出了Looper和Handler的概念,并在Looper和Handler概念的基础上,通过View的处理框架,十分完美地实现了消息分发的功能。下面对这几个概念进行详细介绍。
2.2.1Message
消息对象,它是信息的载体,线程间通讯的数据单元。例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程。
Message通常存放在消息队列(MessageQueue)中,一个MessageQueue可以包含多个Message。
创建实例,obtain(),该方法有多个重载,不一一介绍。
public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; sPoolSize--; return m; } } return new Message(); }
该方法获取Message时并不是直接创建一个新的实例,而是先从Message Pool(消息池)中查看有没有可用的Message实例,如果有,则直接复用这个Message实例;如果没有,创建一个新的Messages实例。
消息回收,recycle()
public void recycle() { clearForRecycle(); synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
系统处理完消息之后,并不是直接将消息消除,而是放到消息池当中(最大值为50个,若消息池中已经有50个Message,则丢弃不保存)。
2.2.2MessageQueue
是一种数据结构,具有先进先出的特点,用来存放消息队列。每个线程最多拥有一个MessagQueue。创建线程时,并不会自动创建对应的MessageQueue。通常使用Looper对象对线程的MessageQueue进行管理。Android应用程序在创建主线程时,会默认创建一个Looper对象,该对象创建时,会自动创建一个MessageQueue。其他线程不会自动创建Looper,需要的时候可以通过调用Looper的prepare()函数创建。
MessageQueue封装在Looper中,用户一般很少去接触,不再详细介绍。
2.2.3Looper
MessageQueue的管理者,每一个MessageQueue都不能脱离Looper而单独存在。
Looper对象的创建是通过prepare函数来实现的。同时每一个Looper对象和一个线程关联。通过调用Looper.myLooper()可以获得当前线程的Looper对象 。创建一个Looper对
public void recycle() { clearForRecycle(); synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
Looper对象创建好后,Looper线程要真正工作,需要调用loop()方法,它不断从自己的MQ中取出队头的消息,将Message交给Message的target进行处理,处理完之后,调用Message.recycle()放入消息池中,消息队列中没有消息时会退出循环。
public static voidloop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("NoLooper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of thisthread is that of the local process, // and keep track of what that identitytoken actually is. Binder.clearCallingIdentity(); final long ident =Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); //might block if (msg == null) { // No message indicates thatthe message queue is quitting. return; } ................................. msg.target.dispatchMessage(msg); ................................. msg.recycle(); } }
除了主线程有默认的Looper,其他线程默认是没有Looper对象的,所以,不能接受Message。如需要接受,自己定义一个Looper对象(通过prepare函数),这样该线程就有了自己的Looper对象和MessageQueue数据结构了。 具有Looper对象的线程我们也称之为“Looper线程”。
如何创建一个Looper线程?
public classLooperThread extends Thread { @Override publicvoid run() { // 将当前线程初始化为Looper线程 Looper.prepare(); // ...其他处理,如实例化handler // 开始循环处理消息队列 Looper.loop(); } }
但是Android是一个比较成熟的系统,所以我们一般不直接去创建Looper线程,android提供了HandlerThread,这是一个带有消息循环的线程,它有自己的消息队列,能够接收其他线程发送的消息。
除了prepare()和loop()方法,Looper类还提供了一些有用的方法,
public static final Looper myLooper() { // 在任意线程调用Looper.myLooper()返回的都是那个线程的looper return(Looper)sThreadLocal.get(); } public Thread getThread() {//得到looper对象所属线程 return mThread; } public void quit() {//结束looper循环 // 创建一个空的message,它的target为NULL,表示结束循环消息 Message msg = Message.obtain(); // 发出消息 mQueue.enqueueMessage(msg, 0); }
通过以上的一些介绍,对Looper可以总结为以下几点:
●每个线程有且最多只能有一个Looper对象;
●Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行;
●Looper使一个线程变成Looper线程。
那么,如何将将消息添加到消息队列以及处理消息呢?且看下文。
2.2.4Handler
消息的发送者和处理者,扮演了往MQ上添加消息和处理消息的角色(只处理由自己发出的消息)。
Handler对象通过obtainMessage()方法,将需要传递的信息封装成Message对象,调用sendMessage()方法将消息传递给Looper,然后由Looper将Message放入MessageQueue中,具体操作在Looper中已经介绍,不再赘述。最后通过Message对应的Handler的handleMessage()进行处理。
Handler只能在它所在的线程上下文中取得消息队列,然后对消息队列操作,如果外部线程需要向某个线程发送消息,必须先获取某个线程中的任意Handler对象,然后通过Handler对象进行发送消息或者删除消息。
Handler创建时会关联一个Looper,默认的构造方法将关联当前线程的looper,不过这也是可以设定的。
public Handler(Callback callback, booleanasync) { .................... mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handlerinside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } public Handler(Looper looper, Callbackcallback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; }
Handler发送消息
Handler使用 post(Runnable), postAtTime(Runnable,long),
postDelayed(Runnable,long), sendEmptyMessage(int),sendMessage(Message), sendMessageAtTime(Message,long)和 sendMessageDelayed(Message,long)这些方法向MQ上发送消息,光看这些API你可能会觉得handler能发两种消息,一种是Runnable对象,一种是Message对象,这是直观的理解,但其实post发出的Runnable对象最后都被封装成message对象了。以post(Runnable r)为例
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static MessagegetPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
Handler发送的消息有如下特点:
●message.target为该handler对象,这确保了looper执行到该message时能找到处理它的handler,即loop()方法中的关键代码;
●post发出的Message,其callback为Runnable对象。
Handler处理消息
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if(mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
dispatchMessage()方法里,如何处理Message则由用户指定,三个判断,优先级从高到低:
●Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作;
●Handler里面mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理;
●处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。
通过对以上几个概念的介绍,我们对消息的处理流程有了一定理解,其流程基本可以概括为一下几点:
●包装Message对象(指定Handler、回调函数和携带数据等);
●通过Handler的sendMessage()等类似方法将Message发送出去;
●在Handler的处理方法里面将Message添加到Handler绑定的Looper的MessageQueue;
●Looper的loop()方法通过循环不断从MessageQueue里面提取Message进行处理,并移除处理完毕的Message;
●通过调用Message绑定的Handler对象的dispatchMessage()方法完成对消息的处理。
我们可以用下图来表示android的消息模型
图2.2 Android消息模型