美文网首页线程
Android Handler消息机制源码分析

Android Handler消息机制源码分析

作者: 艾瑞达双鱼 | 来源:发表于2021-03-15 23:24 被阅读0次

    一、概述

    如果要使用handler发送消息,每一个线程都应该初始化一个Looper对象,调用Looper对象的loop方法,循环的从MessageQueue中取得Message,然后通过Handler的dispatchMessage方法处理此消息,一般的,此方法的最终逻辑会执行我们实例化Handler对象时,重写的handleMessage方法里的逻辑代码。这里涉及到了消息机制的几个核心类:Handler,Message,MessageQueue,Looper。

    二、源码分析

    一般我们首先会实例化一个Handler的对象,所以先看Handler的构造方法:

    public Handler(@Nullable Callback callback, boolean async) {
            if (FIND_POTENTIAL_LEAKS) {
                final Class<? extends Handler> klass = getClass();
                if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                        (klass.getModifiers() & Modifier.STATIC) == 0) {
                    Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                        klass.getCanonicalName());
                }
            }
    
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    
    

    关键在这两句代码,mLooper = Looper.myLooper()拿到了looper的对象, mQueue = mLooper.mQueue拿到了MessageQueue的对象,然后先看Looper的myLooper方法:

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

    它是通过ThreadLocal的一个get存根获取到的Looper的对象,那么这个对象是在什么时候设置呢?我们知道每个线程都必须初始化一个Looper对象,不然会报错:Can't create handler inside thread that has not called Looper.prepare(),那么我们先看一下UI线程是怎么初始化Looper对象的,在ActivityThread里:

    public static void main(String[] args) {
            ......
    
            Looper.prepareMainLooper();
    
            // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
            // It will be in the format "seq=114"
            long startSeq = 0;
            if (args != null) {
                for (int i = args.length - 1; i >= 0; --i) {
                    if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                        startSeq = Long.parseLong(
                                args[i].substring(PROC_START_SEQ_IDENT.length()));
                    }
                }
            }
            ......
    
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
    
            ......
            Looper.loop();
    
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    

    我们发现有一个 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();
            }
        }
    
      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的prepare方法,在这个方法里,给ThreadLocal设置了一个Looper的对象,也就相当于把一个线程同一个Looper对象绑定在了一起,而通过Looper的构造方法我们知道了MessageQueue的对象是在初始化Looper对象的时候实例化的:

    private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    

    需要说明的是,在主线程里API已经调用了Looper.prepare方法,但是在子线程里没有调用,所以我们需要自己手动调用Looper.prepare方法。接下来我们了解一下Looper到底是怎么从MessageQueue里拿消息的,通过ActivityThread的main方法,我们知道了Looper.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;
    
           ......
    
            for (;;) {
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
    
               ......
                try {
                    msg.target.dispatchMessage(msg);
                    if (observer != null) {
                        observer.messageDispatched(token, msg);
                    }
                    dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                } catch (Exception exception) {
                    if (observer != null) {
                        observer.dispatchingThrewException(token, msg, exception);
                    }
                    throw exception;
                } finally {
                    ThreadLocalWorkSource.restore(origWorkSource);
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                ......
            }
        }
    

    这是一个死循环,循环的从MessageQueue里取出消息,并通过 msg.target.dispatchMessage(msg)处理拿到的消息,这个msg.target其实就是handler对象,那么handler的对象是在什么时候赋值给message的target属性的呢?我们来看handler的sendMessage方法,也就是发送消息必须调用的方法,最终会调用enqueueMessage方法,代码如下:

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

    这里就是把自身的实例赋值给了message的target,然后通过MessageQueue的enqueueMessage方法,把此消息压入队列当中:

    boolean enqueueMessage(Message msg, long when) {
            if (msg.target == null) {
                throw new IllegalArgumentException("Message must have a target.");
            }
    
            synchronized (this) {
                ......
                if (p == null || when == 0 || when < p.when) {
                    // New head, wake up the event queue if blocked.
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                    ......
                    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;
                }
               ......
            }
            return true;
        }
    

    此处会按照 Message 的时间 when 来有序得插入 MessageQueue 中,可以看出 MessageQueue 实际上是一个有序队列,只不过是按照 Message 的执行时间来排序。至此,整个消息机制就已经基本分析完毕,下面讲一些面试相关的问题。

    三、一些问题

    首先是handler的post和sendMessage 的区别,post方法源码如下:

    public final boolean post(@NonNull Runnable r) {
           return  sendMessageDelayed(getPostMessage(r), 0);
        }
    
    private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            m.callback = r;
            return m;
        }
    
    public void dispatchMessage(@NonNull Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    
    private static void handleCallback(Message message) {
            message.callback.run();
        }
    

    这里可以看出,post方法把传入的Runnable 对象设置给了Message的callback,而在handler的dispatchMessage方法里,如果 Message 的 Callback 不为 null,一般为通过 post(Runnabl) 方式,会直接执行 Runnable 的 run 方法。因此这里的 Runnable 实际上就是一个回调接口,跟线程 Thread 没有任何关系。

    其次就是Looper.loop() 为什么不会阻塞主线程,刚才我们了解了,Looper 中的 loop 方法实际上是一个死循环。但是我们的 UI 线程却并没有被阻塞,反而还能够进行各种手势操作,这是为什么呢?在 MessageQueue 的 next 方法中,有如下一段代码:

    for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }
    
                nativePollOnce(ptr, nextPollTimeoutMillis);
             ......
       }
    

    nativePollOnce是一个本地native方法,当调用此 native 方法时,主线程会释放 CPU 资源进入休眠状态,直到下条消息到达或者有事务发生,通过往 pipe 管道写端写入数据来唤醒主线程工作,这里采用的 epoll 机制,我也不是很清楚这个机制的具体逻辑,有知道的同学可以补充一下。

    相关文章

      网友评论

        本文标题:Android Handler消息机制源码分析

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