Android消息机制笔记

作者: CSU_IceLee | 来源:发表于2017-07-20 20:35 被阅读76次

Handler

  1. Handler的声明:

        Handler handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                return false;
            }
        });
    

    其中构造方法可以不传参数,可以像上面一样传入Handler.Callback,也可以传入Looper

  2. 使用:

    handler.post(new Runnable() {
        @Override
        public void run() {
            Log.d(TAG, "run: currentThread:"+Thread.currentThread());
        }
    });
    

    这里打印的线程是主线程

    还可以使用hander.sendMessage等一系列方法,这种方式会回调创建匿名类中的handleMessage方法中。

  3. 不管是post还是sendMessage,最终都会调用到Handler的sendMessageAtTime方法:

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            MessageQueue queue = mQueue;
            if (queue == null) {
                RuntimeException e = new RuntimeException(
                        this + " sendMessageAtTime() called with no mQueue");
                Log.w("Looper", e.getMessage(), e);
                return false;
            }
            return enqueueMessage(queue, msg, uptimeMillis);
        }
        
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    可以发现是往queue消息队列中加入了一条消息!!!

  4. 那么当有消息来的时候,Looper会调用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是我们post方法传入的runnable对象。

    mCallback是创建Handler的时候传入的Callback对象。

    所以我们发送消息时候会回调我们实现的Callback中的handleMessage方法。

    最后的handleMessage方法是一个空方法。

MessageQueue

MessageQueue是消息队列,但是内部实现为单链表,主要是通过next方法取消息,并将消息移出队列。next方法中是一个无限的循环,如果没有消息,将会阻塞。

enqueueMessage方法用于插入消息到队列之中。

Looper

looper是消息循环,不断的从MessageQueue中查看是否有新消息,如果有就立刻处理,如果没有就阻塞。

在子线程中创建一个Looper:

        new Thread("Thread #2"){
            @Override
            public void run() {
                Looper.prepare(); #为当前线程创建一个Looper
                Handler handler = new Handler();
                Looper.loop(); #开启消息循环  会阻塞线程
            }
        }.start();

Looper的prepare方法会转到另一个重载方法,将Looper对象加入sThreadLocal:

    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声明如下,泛型是Looper:

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

然后new Handler()最终会转到这个构造方法:

public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
        ...
        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 = callback;
        mAsynchronous = async;
    }

其中的Looper.myLooper()的方法实现如下:

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

所以,如果在子线程中没有调用Looper.prepare(),那么sThreadLocal里面保存的就会为空,那么会抛出运行时异常:java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

接下来看看loop方法:

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.recycleUnchecked();
        }
    }

可以看到loop方法里面有一个无限的循环,一直向queue消息队列获取消息,如果没有消息,那么会阻塞,有了消息之后会调用msg.target.dispatchMessage(msg);分发消息,msg.target就是Handler对象,这个在一开始就分析了Handler分发消息的过程。

相关文章

网友评论

    本文标题:Android消息机制笔记

    本文链接:https://www.haomeiwen.com/subject/favjkxtx.html