Android消息循环机制探讨

怎样理解Android中的Handler,MessageQueue、Runnable与Looper?

简单来讲,用一句话概括就是:
Looper不断获取MessageQueue中的一个Message(Runnable会包装成Message),然后由Handler来处理。

这其实就是进程/线程跑起来的基础,我们称为消息循环处理机制。其实任何系统能够跑起来的本质就是依赖于这样一个机制。

所以我们第一个要搞清楚的问题是在Android中消息循环处理机制是怎么搭建起来的?

线程是CPU调度的基本单位,也就是说线程相当于一个平台,我们利用这个平台做各种各样的事情,显然消息循环机制(Looper)也是在线程上完成的。所以我们首先看下在线程里是怎么使用Looper的?举个简单的例子:

class LooperThread extends Thread{
        @Override
        public void run() {
            Looper.prepare();
            Handler h = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                }
            };
            Looper.loop();
        }
    }

从上面的代码可以看出,Looper的使用是非常简单的,就3个步骤。

第一步:Looper.prepare();
准备工作,看下源码里面做了什么事情?

public static void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
}

private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }

首先看下sThreadLocal对象是什么东西?

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

哦,原来是ThreadLocal对象,这里ThreadLocal的意义在于为每个线程隔离Looper对象,也就是每个线程对应一个Looper对象,sThreadLocal就是用来统一管理每个线程的Looper对象,确保每个线程的Looper对象是独立的。所以Looper.prepare();的作用在于为线程创建一个Looper对象,可以通过sThreadLocal.get()来获得。从代码里可以看出,一个线程对应一个Looper对象。然后在Looper的构造器里初始化了MessageQueue。

第二步:创建处理消息的Handler;
你肯猜到了,Handler和Looper存在某种关系,不然这个消息循环处理是怎么连接起来的。所以我们从Handler的构造方法入手。

public Handler() {
       //省略部分代码
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = null;
    }

从上面的代码里我们得到以下几条信息:
1、在Handler里通过Looper.myLooper()得到了线程的Looper对象;

public static Looper myLooper() {
        return sThreadLocal.get();
}

2、在Handler里维护了一个MessageQueue mQueue,指向Looper对象里的MessageQueue。
3、有一个回调接口对象 mCallback

好,现在Handler、Looper和MessageQueue算是有了一些联系。

下面看第三步:Looper.loop();
前面两步都是准备工作,这一步开始真正做事情了,我们来看下loop()方法里的主要流程。

public static void loop() {
        Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        MessageQueue queue = me.mQueue;

        while (true) {
            Message msg = queue.next(); // might block
            if (msg != null) {
                if (msg.target == null) {
                    return;
                }
                msg.target.dispatchMessage(msg);

                msg.recycle();
            }
        }
    }

从上面可以看出,消息循环的机制就是从Looper对象中拿到消息队列MessageQueue,然后不断的从MessageQueue里拿到消息,然后由handler(msg.target)去处理(dispatchMessage(msg))。

从创建Looper对象和Handler对象,到handler和Looper之间联系起来,再到消息循环处理,就这样,消息循序处理机制搭建起来了,线程跑起来了,整个流程我们似乎也明白了,但其实仔细想想,我们忽略了很多细节:

1、前面我们讲了从消息队列MessageQueue中取出消息message交由handler处理,但好像没看到过消息是怎么放进消息队列的?Message和handler是怎么联系起来的也不清楚?

我们知道handler是用来处理消息的,其实handler还有另外一个作用—-发送消息:将Message压入MessageQueue中。Handler发送消息用到的API分为两个系列,Post系列和Send系列。
Post系列:
public final boolean post(Runnable r);
public final boolean postAtTime(Runnable r, long uptimeMillis);
…………
Send系列:
public final boolean sendMessage(Message msg);
public final boolean sendEmptyMessage(int what);
public final boolean sendMessageDelayed(Message msg, long delayMillis);
public boolean sendMessageAtTime(Message msg, long uptimeMillis);
……………
Post和Send两个系列的共同点是它们都负责将某个消息压入MessageQueue中;区别在于后者处理的函数参数直接是Message,而Post处理的参数是Runnable,将Runnable转换成Message,再调用Send系列函数来执行下一步。
我们挑选第一个Post函数来分析下其流程:

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
}

首先通过方法getPostMessage(r)将Runnable对象封装成Message ,再通过对应的send函数把它推送到MessageQueue中;

private final Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
}

从getPostMessage(r)方法中我们可以知道以下几点
1)Android系统会维护一个全局的Message池,当用户需要使用Message时,可以通过obtain直接获得,而不是自行创建。这样的设计可以避免不必要的资源浪费。
2)Runnable对象赋给的Message的callback,在Message类中callback的定义如下:
Runnable callback;

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

SystemClock.uptimeMillis():从手机开机到现在的毫秒数,不包括睡眠时间。

public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
            msg.target = this;
            sent = queue.enqueueMessage(msg, uptimeMillis);
        }
        return sent;
}

还记得mQueue吗?在handler的构造器中将Looper里的MessageQueue赋给了mQueue。
Message和handler也在这里联系起来了:msg.target = this;
最后queue.enqueueMessage(msg, uptimeMillis)将消息压入队列。

2、对于handler处理消息,上面我们是一笔带过的,msg.target.dispatchMessage(msg); 那消息处理的流程到底是怎么样的呢?
在Handler类中对消息处理有两个方法:
public void dispatchMessage(Message msg);//对Message进行分发
public void handleMessage(Message msg);//对Message进行处理
Looper从MessageQueue中取出一个Message后,首先会调用Handler. dispatchMessage进行消息派发,

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

首先判读msg.callback是否为空,还记得这个吗?前面提到过msg.callback就是Post系列传入的Runnable对象,所以Post系列发送的消息msg.callback不为空,走handleCallback(msg),

private final void handleCallback(Message message) {
        message.callback.run();
}

直接调用run方法执行任务,消息处理结束。
Send系列发送的消息callback为空,所以走else,先判断mCallback是否为空,mCallback对象是什么?其实这是一个回调接口对象,接口里也有一个handleMessage(msg)方法用来处理消息,

public interface Callback {
        public boolean handleMessage(Message msg);
}

这个对象是在Handler的构造器中传入的,传了就有,没传就没有,作用在于有了回调接口来处理消息,就不用写Handler的子类来实现handleMessage(Message msg)方法了。
如果mCallback为空,或者mCallback.handleMessage(msg)消息没处理成功,就走Handler类中的handleMessage(msg)了,

public void handleMessage(Message msg) {
}

在Handler类中,这是一个空的方法,子类重写这个方法去完成消息处理的逻辑。
讲到这里,你可能会很奇怪,Handler同时负责发送消息和处理消息,为什么不直接操作,而是大费周章地先把Message压入MessageQueue,再取出来处理?其实这体现了程序设计的一个良好的习惯,即“有序性”。如果世界没有了秩序,那将乱的一塌糊涂,程序也是如此。

3、我们知道,调用Looper.loop()后消息循环处理就开始了,那循环什么时候退出了?
从loop()方法里可以看到有一种情况可以退出循环,就是当msg.target == null ;时,但是上面我们讲过msg.target的赋值是在sendMessageAtTime(Message msg, long uptimeMillis)方法里,而不管是Post还是Send发送消息都会走到这个方法,从而给msg.target赋值,所以正常情况下消息处理循环是不会退出的,怎么办了?其实在Looper类中有一个quit()方法,我们看下里面做了什么事情?

public void quit() {
        Message msg = Message.obtain();
        mQueue.enqueueMessage(msg, 0);
    }

在这里面直接往MessageQueue中压入一条消息,这样msg.target就为空了,当循环到这条消息时,就退出了循环,所以如果你想终止消息处理循环,请调用这个方法即可。

现在,我们基本上理清了Android中的Handler,MessageQueue、Runnable与Looper之间的关系和它们之间的流程机制了。每个Thread只对应一个Looper;每个Looper只对应一个MessageQueue。把一个Thread比作一座城市,Looper是城市的户籍名称;MessageQueue是这座城市的唯一地标性建筑;而Hander就是人口,在一座城市,可以有本地人口,也可以有外地人口,但每个人都有户籍,标识他属于哪座城市;Message就是货物,不区分属地,携带在人的身上,在城市或城市间流通。
最后提一点,在Looper类中还有这样两个方法:

public static void prepareMainLooper() {
        prepare();
        setMainLooper(myLooper());
        myLooper().mQueue.mQuitAllowed = false;
}

public synchronized static Looper getMainLooper() {
        return mMainLooper;
}

这两个方法就是我们熟悉的UI主线程专用的方法了,
new Handler(getMainLooper())就代表在主线程运行了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值