Handler,Messgae,MessageQueue,Looper分析

来源:互联网 发布:淘宝店等级划分 编辑:程序博客网 时间:2024/05/19 14:52

首先,明确一点,一个线程只能创建一个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再被回收重置状态供下一次使用

0 0
原创粉丝点击