消息模型

一、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,则丢弃不保存)。

这么做的好处就是消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。

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消息模型

更多相关文章

  1. android:GLSurfaceView绘制bitmap图片及glViewport调整的效果
  2. Android对象序列化(Activity之间传递对象,Parcelable小例子)
  3. 【Android】Android(安卓)线程&进程模型
  4. android中webView使用总结---飞速浏览器实现基础.
  5. android中activity无法启动的原因小结
  6. Android(安卓)IntentService的使用与源码解析
  7. 8.16 提炼参数对象
  8. Android中Calendar类的用法总结
  9. 详解Android(安卓)中AsyncTask 的使用

随机推荐

  1. android 游戏导引(1. 建立 OpenGL 项目)
  2. Android 从properties配置文件读取数据
  3. Android布局layout中的一些属性(转)
  4. AirFree远程协助(Android)
  5. Android应用层学习-Intent和Layout
  6. Android上常见度量单位【xdpi、hdpi、mdp
  7. Android 中如何关闭线程
  8. Android运行底层linux外部命令的实现
  9. Intent----android中的伟大邮差
  10. Android(安卓)远程图片获取和本地缓存