Android消息机制

一、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.1 Message

消息对象,它是信息的载体,线程间通讯的数据单元。例如后台线程在处理数据完毕后需要更新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.2 MessageQueue

是一种数据结构,具有先进先出的特点,用来存放消息队列。每个线程最多拥有一个MessagQueue。创建线程时,并不会自动创建对应的MessageQueue。通常使用Looper对象对线程的MessageQueue进行管理。Android应用程序在创建主线程时,会默认创建一个Looper对象,该对象创建时,会自动创建一个MessageQueue。其他线程不会自动创建Looper,需要的时候可以通过调用Looper的prepare()函数创建。

MessageQueue封装在Looper中,用户一般很少去接触,不再详细介绍。

2.2.3 Looper

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 void loop() {
        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 class LooperThread 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.4 Handler

消息的发送者和处理者,扮演了往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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值