Android消息循环

为什么一个线程需要消息循环和消息处理:
如果这个线程需要处理的工作是不需要与其它线程交互的,简单的工作,那么你可以直接用thread,不需要引入消息循环与处理机制。
但是如果你的线程需要与其它的线程交互,特别是线程本身要处理的工作是分步的,
且各部分工作间是有一个相互状态依赖关系的时候,引入消息循环与处理机制就非常有必要。

消息循环与处理机制的引入,让线程的控制和交互变得友好。

构成消息机制的几个类:
消息队列 MessageQueue
消息泵 Looper
消息处理 Handler
线程 Thread

消息队列是用来管理消息的,消息泵是用来提取消息的,而Handler是用来处理消息的,线程是一个运行的载体。

看一个实例:
ActivityThread.main:

    public static void main(String[] args) {
        ...

        Looper.prepareMainLooper();

       ...
        Looper.loop();

    }

这个例子是用来创建activity主线程(UI线程)的消息循环的。

第一步:Looper.prepareMainLooper()

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

prepare(false)是为线程创建了一个消息泵,并把它保存在线程对应的数据中。

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

sThreadLocal是一个静态成员变量:static final ThreadLocal sThreadLocal = new ThreadLocal();
这样的设计说明它是进程内共享的,这里说保存在线程独有的数据存储中,是因为它为每个线程都保存了一个以线程为KEY的映射的数据。这样,当需要取某个线程的数据时,只要得到当前的线程,来找到这个映射的数据。

总之,这样线程与looper之间就捆绑在一起了,另外这个prepare函数只能调用一次。因为如果sThreadLocal.get() != null就会抛出异常,这说明一个thread只能对应一个looper

第二步:Looper.loop()

    public static void loop() {
        final Looper me = myLooper();
    ...
        final MessageQueue queue = me.mQueue;

    ...
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

           ...

            msg.target.dispatchMessage(msg);

           ...
            msg.recycle();
        }
    }

先通过myLooper()得到当前thread的looper,然后得到它的消息队列。这个消息队伍是在looper构造的时候,就已经创建了:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//消息队列
mThread = Thread.currentThread();//得到当前线程
}
接下来进入一个死循环当中,不断的从队列中取出消息。
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {

nativePollOnce(mPtr, nextPollTimeoutMillis);//nextPollTimeoutMillis为等待的时间

        synchronized (this) {
           ...
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {//这里是处理同步消息的
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {//最近的一个消息的时间还没有到达
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {//取消息
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (false) Log.v("MessageQueue", "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

            if (mQuitting) {//退出循环,接到quit消息后,会将mQuitting置为true
                dispose();
                return null;
            }

            ...
    }
}


消息是有时间参数的,按时间的顺序排列。所以在插入消息时,会先比较时间,找到一个比当前消息处理(执行)时间晚的第一个节点插入。

取到消息后,由msg.target.dispatchMessage(msg)对消息进行分发处理。
Message有两个成员,一个是Handler target,另一个 Runnable callback。这两个成员分别对应了两种不同的发送消息方式的处理。
这里先调用了Message的target,这是一个Handler。

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


先是判断callback成员变量不为null时,就调用handleCallback(msg):
private static void handleCallback(Message message) {
    message.callback.run();
}

这个函数是调用了message的callback,是一个Runnable。什么样的情况下,这个message.callback不为空?
Handler.post:
public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

->Handler.getPostMessage:
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

这里可以看出,如果用post的方式来写入消息时,callback就会置上,message.callback.run()调用的是传进来的Runnable的run函数。
如果mCallback 不为空,则调用的是mCallback的handleMessage,什么样的情况下,mCallback 不为空?
这个mCallback只有在Handler构造函数里会置上。看一个实例:

mStartSHandler = new Handler(mStartSThread.getLooper(), mStartSCallback);
private Handler.Callback mStartSCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
final int startId = msg.arg1;
Util.MyLogd(TAG, “mStartSCallback for startId ” + startId+” , msg.what = “+msg.what);

        switch (msg.what) {
        ...
    }
}
 };

在调用的时候,自己定义一个Handler.Callback传进来,这样消息就由自定义的这个Handler.Callback来处理了。

如果mCallback还是为空,就调用自身的handleMessage:
/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}


这里说子类必须要实现这个函数来接收消息。
比如像实例这样:
     mHandlerThread = new HandlerThread(TAG);
     mHandlerThread.start();
     mAsyncHandler = new AsyncHandler(mHandlerThread.getLooper());


class AsyncHandler extends Handler {

@Override
public void handleMessage(Message msg) {
switch (msg.what) {

}
}
}


在继承的子类中去覆写这个函数。
所以,按照自己的实际需求决定使用哪种方式。

上面都是消息的处理(消费过程),下面我们来看一下消息的产生(生产过程)。
两种方式:postsend
Handler.post和Handler.sendMessage:
public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

    ...
public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}


最终调用的都是sendMessageDelayed,只不过post设置了message.callback,并且尝试从当前的全局的池子里取到一个message对象(如果没有,还是要new一个)
然后sendMessageDelayed调用了sendMessageAtTime,最后调用了Handler.enqueueMessage:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

转到MessageQueue.enqueueMessage:
boolean enqueueMessage(Message msg, long when) {
...

    synchronized (this) {
        if (mQuitting) {//已经处于退出的状态中
            return false;
        }

        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

    // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

“`

这个函数将msg加入到消息队列当中去,if (p == null || when == 0 || when < p.when)如果当前的队列是空的,那么这个msg就是队首,如果是立即执行的消息,那么这个msg也要放在队首。如果执行的时间比当前队首的时间还要早,那这个消息也应该要放在队首。

如果不是以上几种情况的话,那就要在整个队列中遍历,直到找到比msg的执行时间要晚的节点,然后插入在它的前面。

这说明,整个消息队列是按执行时间的顺序来排序的。needWake是一个用来唤醒的标识。这是因为,如果当前的消息队列中的消息都还没有到达执行时间时,线程就会处于等待状态。

最后,总结一下:
1、为线程创建了一个消息泵Looper,这个消息泵创建的构造函数中会创建一个对应的消息队列MessageQueue,并将线程与消息泵关联起来。同时以thread为key,将对象保存在一个全局的静态变量sThreadLocal中方便提取和管理
2、使用Looper.loop()进入到消息循环当中,不断的从消息队列中提取Message并分发处理。
3、提取出来的Message会将由Message的target或callback成员来处理。它们分别对应的是Handler和Runnable
4、产生消息是使用了Handler的postXX或sendXX两种方式,最终会把消息按时间顺序加入到队列中去。post和send的执行体分别对应Runnable和Handler。

另外,sThreadLocal存在一个内存泄露的问题,后续再分析。

附类图(注意相互之间的关联关系):
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值