Handler,Messgae,MessageQueue,Looper分析

首先,明确一点,一个线程只能创建一个Looper,只有一个MessageQueue,但是可以有多个Handler来发消息

Looper

Looper里面包装了一个消息队列MessageQueue,Looper.prepare(),方法是用来初始化该线程的Looper信息,例如new一个Looper出来,然后保存到ThreadLocal,这样可以确保这个线程只有一个Looper,因为ThreadLocal可以实现线程隔离;其中的核心方法就是loop(),此方法就是循环的从MessageQueue中取出消息出来然后处理:

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

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

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

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycle();
        }
    }

如上可知,在for死循环之中反复的去消息,queue.next()取出下一条消息,然后调用Message中的target(Handler)的dispatchMessage()方法:

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

由代码可知Looper取出消息后会执行到这里,而Handler发送的Message如果有设置callback(Runnable)的话就会直接执行run方法,如果Handler有设置Callback那就直接调用Callback的handleMessage方法直接把消息处理了,只有以上两者条件都不满足才会最后去调用Handler的handleMessage()方法,而Handler的handleMessage()方法就是我们自己去重载的方法,到这消息处理结束,然后进入下一个循环;
这里面需要注意一个问题:在线程中初始化Looper的步骤

class LooperThread extends Thread {
        public Handler mHandler;

        public void run() {
            Looper.prepare();

            //do something

            Looper.loop();
        }
    }

必须这么调用,因为loop是死循环,所以在loop方法后面的代码都不能执行到,并且prepare()方法再一个线程中不能重复调用两次,会报错的“Only one Looper may be created per thread”

public static void prepare() {
        prepare(true);
    }

    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));
    }

就是这段代码控制一个线程只能有一个Looper的

Handler

handler负责往消息队列里面发送消息

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

所有的sendMessage方法或者post方法最终都是调用以上方法把Message压入线程中对应的MessageQueue的尾部,另外声明一点,Handler中的一些延迟发送Message例如sendMessageDelay等方法在cup休眠的时候是会跟着休眠,暂停执行,要等cup唤醒后才重新开始计时延迟,然后再执行

Message

在创建消息的时候最好调用Handler.obtain()或者Message.Obtain()方法来获取一个可用的消息,尽量不要选择去new一个新的Message;因为在Looper处理完消息后会调用Message.recycle()方法:

public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

public void recycle() {
        clearForRecycle();

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

void clearForRecycle() {
        flags = 0;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        when = 0;
        target = null;
        callback = null;
        data = null;
    }

clearForRecycle()是把被处理过的Message中的状态全部都设置成初始状态,然后把当前Message对象赋值给sPool,表示当前这个Message是可用的空消息,在obtain()里面就会去判断是否存在可用消息,如果存在那么久直接取出来而不用再去new Message()占内存

Handler和HandlerThread

在主线程new出来的Handler默认都已经有mainLooper了,所以可以直接发消息执行,而且Handler的handleMessage()等消息处理方法都是运行在主线程中的,所以不能执行耗时操作,执行耗时操作会有可能造成界面卡顿报错,所以我们一般用来刷新UI;而android提供了HandlerThread来让我们可以执行耗时操作,HandlerThread是一个单独的线程,所以它有自己单独的Looper,但是必须先start线程,初始化这个Looper,然后new Handler(HandlerThread.getLooper())把这个线程的Looper转入指定的Handler,这样handler发的消息就都是发到这个Looper的MessageQueue里面来处理了,而Looper是在HandlerThread这个单独线程里面的所以在loop()里面处理Message的时候并不会阻塞主线程,但是能在这个handler里面执行UI刷新操作吗?可以的。HandlerThread实现其实和以下这种方式是一样的:

Looper.prepare();

            //do something

            Looper.loop();

只不过HandlerThread多了一个可以执行耗时操作功能而已

总结

一个线程只能有一个Looper,一个Looper维护一个MessageQueue,handler就负责往MessageQueue丢消息,Looper就负责循环的从MessageQueue中取消息出来处理,每个Message中的target默认都是发送这个Message的Handler,所以消息被取出来之后就直接交由Handler去调用dispatchMessage()分发处理消息,然后Message再被回收重置状态供下一次使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值