美文网首页
这一次,我们用最详细的方式解析Android消息机制的源码

这一次,我们用最详细的方式解析Android消息机制的源码

作者: Android架构木木 | 来源:发表于2019-06-13 18:13 被阅读0次

    Handler源码解析

    一、创建Handler对象

    使用handler最简单的方式:直接new一个Handler的对象

    Handler handler = new Handler();
    

    所以我们来看看它的构造函数的源码:

     public Handler() {
            this(null, false);
        }
    
        public Handler(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 that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    这段代码做了几件事:

    1、校验是否可能内存泄漏
    2、初始化一个Looper mLooper
    3、初始化一个MessageQueue mQueue

    我们一件事一件事的看:

    1、校验是否存在内存泄漏

    Handler的构造函数中首先判断了FIND_POTENTIAL_LEAKS的值,为true时,会获取该对象的运行时类,如果是匿名类,成员类,局部类的时候判断修饰符是否为static,不是则提示可能会造成内存泄漏。

    问:为什么匿名类,成员类,局部类的修饰符不是static的时候可能会导致内存泄漏呢?

    答:因为,匿名类,成员类,局部类都是内部类,内部类持有外部类的引用,如果Activity销毁了,而Hanlder的任务还没有完成,那么Handler就会持有activity的引用,导致activity无法回收,则导致内存泄漏;静态内部类是外部类的一个静态成员,它不持有内部类的引用,故不会造成内存泄漏

    这里我们可以思考为什么非静态类持有外部类的引用?为什么静态类不持有外部类的引用?

    问:使用Handler如何避免内存泄漏呢?
    答:使用静态内部类的方式

    2、初始化初始化一个Looper mLooper

    这里获得一个mLooper,如果为空则跑出异常:

    "Can't create handler inside thread that has not called Looper.prepare() "
    

    如果没有调用Looper.prepare()则不能再线程里创建handler!我们都知道,如果我们在UI线程创建handler,是不需要调用这个方法的,但是如果在其他线程创建handler的时候,则需要调用这个方法。那这个方法到底做了什么呢?我们去看看代码:

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

    先取sThreadLocal.get()的值,结果判断不为空,则跑出异常“一个线程里只能创建一个Looper”,所以sThreadLocal里存的是Looper;如果结果为空,则创建一个Looper。那我们再看看,myLooper()这个方法的代码:

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

    总上我们得出一个结论:当我们在UI线程创建Handler的时候,sThreadLocal里已经存了一个Looper对象,所以有个疑问:
    当我们在UI线程中创建Handler的时候sThreadLocal里的Looper从哪里来的?
    我们知道,我们获取主线程的Looper需要调用getMainLooper()方法,代码如下:

    public static Looper getMainLooper() {
            synchronized (Looper.class) {
                return sMainLooper;
            }
        }
    

    所以我们跟踪一下这个变量的赋值,发现在方法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),这个方法我们刚才已经看了,是创建一个Looper对象,然后存到sThreadLocal中;\
    • 然后判断sMainLooper是否为空,空则抛出异常
    • sMainLooper不为空,则sMainLooper = myLooper()
      至此sMainLooper对象赋值成功,所以,我们需要知道prepareMainLooper()这个方法在哪调用的,跟一下代码,就发现在ActivityThread的main方法中调用了Looper.prepareMainLooper();。现在真相大白:
      当我们在UI线程中创建Handler的时候sThreadLocal里的Looper是在ActivityThread的main函数中调用了prepareMainLooper()方法时初始化的
      ActivityThread是一个在一个应用进程中负责管理Android主线程的执行,包括活动,广播,和其他操作的类

    3、初始化一个MessageQueue mQueue

    从代码里我们看出这里直接调用了:mLooper.mQueue来获取这个对象,那这个对象可能在Looper初始化的时候就产生了。我们去看看Looper的初始化代码:

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

    代码很简单,就是创建了MessageQueue的对象,并获得了当前的线程。

    至此,Handler的创建已经完成了,本质上就是获得一个Looper对象和一个MessageQueue对象!

    二、使用Handler发送消息

    Handler的发送消息的方式有很多,我们跟踪一个方法sendMessage方法一直下去,发现最后竟然调用了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);
        }
    

    这段代码做了三件事:

    1、给msg.target赋值,也就是Handler对象
    2、给消息设置是否是异步消息。
    3、调用MessageQueue 的enqueueMessage(msg, uptimeMillis)方法
    我们只关注第三步:这一步把Handler的发送消息转给了MessageQueue的添加消息的方法。
    所以至此,Handler发送消息的任务也已经完成了,本质上就是调用MessageQueue自己的添加消息的方法!

    三、MessageQueue添加消息

    MessageQueue的构造函数代码如下:

    MessageQueue(boolean quitAllowed) {
            mQuitAllowed = quitAllowed;
            mPtr = nativeInit();
        }
    

    也没做什么特别的事情。我们去看看enqueueMessage(msg, uptimeMillis)方法代码:

    boolean enqueueMessage(Message msg, long when) {
            if (msg.target == null) {
                throw new IllegalArgumentException("Message must have a target.");
            }
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
    
            synchronized (this) {
                if (mQuitting) {
                    IllegalStateException e = new IllegalStateException(
                            msg.target + " sending message to a Handler on a dead thread");
                    Log.w(TAG, e.getMessage(), e);
                    msg.recycle();
                    return false;
                }
    
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                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 {
                    // Inserted within the middle of the queue.  Usually we don't have to wake
                    // up the event queue unless there is a barrier at the head of the queue
                    // and the message is the earliest asynchronous message in the queue.
                    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;
        }
    

    代码很长,但是通过观察这段代码我们发现这个MessageQueue实际上是个链表,添加消息的过程实际上是一个单链表的插入过程。

    所以我们知道了Handler发送消息的本质其实是把消息添加到MessageQueue中,而MessageQueue其实是一个单链表,添加消息的本质是单链表的插入

    四、从消息队列里取出消息

    我们已经知道消息如何存储的了,我们还需要知道消息是如何取出的。
    所以我们要看一下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);
                }
    
            }
        }
    

    代码太长我删了部分代码。可以看出这个方法主要的功能是很简单的。

    • 获取Looper对象,如果为空,抛异常。
    • 获取消息队列MessageQueue queue
    • 遍历循环从消息队列里取出消息,当消息为空时,循环结束,消息不为空时,分发出去!
      但是实际上当没有消息的时候queue.next()方法会被阻塞,并标记mBlocked为true,并不会立刻返回null。而这个方法阻塞的原因是nativePollOnce(ptr, nextPollTimeoutMillis);方法阻塞。阻塞就是为了等待有消息的到来。那如果在有消息加入队列,loop()方法是如何继续取消息呢?
      这得看消息加入队列的时候有什么操作,我们去看刚才的enqueueMessage(msg, uptimeMillis)方法,发现
    if (needWake) {
        nativeWake(mPtr);
    }
    

    当needWake的时候会调用一个本地方法唤醒读取消息。
    所以这里看一下消息分发出去之后做了什么?

    msg.target.dispatchMessage(msg);
    

    上面讲过这个target其实就是个handler。所以我们取handler里面看一下这个方法代码

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

    代码非常简单,当callback不为空的时候调用callback的handleMessage(msg)方法,当callback为空的时候调用自己的handleMessage(msg)。一般情况下我们不会传入callback,而是直接复写Handler的handleMessage(msg)方法来处理我们的消息。

    写在最后

    想学习更多Android知识,或者获取相关资料请加入Android开发交流群:1018342383。 有面试资源系统整理分享,Java语言进阶和Kotlin语言与Android相关技术内核,APP开发框架知识, 360°Android App全方位性能优化。Android前沿技术,高级UI、Gradle、RxJava、小程序、Hybrid、 移动架构师专题项目实战环节、React Native、等技术教程!架构师课程、NDK模块开发、 Flutter等全方面的 Android高级实践技术讲解。还有在线答疑

    相关文章

      网友评论

          本文标题:这一次,我们用最详细的方式解析Android消息机制的源码

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