欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

从源码的角度分析Android中的Handler机制的工作原理

程序员文章站 2022-07-14 15:09:42
...

                          通过对源码的查看,特此记录Android中handler机制的工作方式

 

我们要在Android中使用Handler机制,必须要经过如下三个过程

  1. 在一个线程调用   Looper.prepare()   来创建一个Looper. (主线程不需要,因为源码中在Activity创建的时候已经调用过一次了)
  2. 调用   new Handler()   创建一个   Handler.
  3. 调用   Lopper.lopp()   开始进入死循环不停的从   MessageQueue   中取出消息然后发送给   Handler   处理.

 

下面我们从源码的角度去查看为什么要经过这三个过程Handler机制才能正常工作:

源码的 Looper.prepare()  干了什么:

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.set(new Looper(quitAllowed))   给一个ThreadLocal变量设置了一个 Lopper  对象,这样就造成了这个线程和这个  Lopper  关联起来了.

从源码中发现,在Handler机制中的   MessageQueue   是由   Lopper  来维护的, 为什么? 因为源码中有这么一段:

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

所以经过源码中的步骤,一个线程就和一个 Lopper 关联起来了,而这个 Lopper 又维护着一个 MessageQueue .

 


源码的 new Handler()  干了什么:

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

这是    Lopper.myLopper  的源码:

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
  • 天哪噜,我们看到,,,之前设置给那个  ThreadLocal  变量的   Lopper  在这里 马上就被使用  sThreadLocal.get() 方法被  Handler 获取到了.
  • 然后我们看到,获取到  Looper 之后马上又获取了 Looper 的 MessageQueue.

所以通过这一步我们看到, 源码帮我们把 我们之前调用的 Looper.prepare 创建的 looper 对象和一个 Handlder 关联起来了,并且Handler 还获取了由 Looper 维护的 MessageQueue!  接下来就可以干大事了!!!

 


源码的  Lopper.lopp()  干了什么(比较多,贴精华部分):

     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);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
}

这里是 Message 类的部分源码,方便理解上面的代码:

class Message implements Parcelable {
  Handler target;
}
  • 我们看到,在loop方法中,也同样是调用了 myLooper 获取了 第一步创建的一个和线程关联的 looper 
  • 然后获取了这个 looper 的 messagequeue ,之后就会进入一个死循环.
  • 在这个死循环中,会不停的去调用 message.next ,来获取下一个 message (message队列使用链表实现的.)
  • 获取到一个 message后,就会调用 message 的  target.dispatchMessage  来发送消息到handler, 通过上面 message 类 ,我们看到 target变量就是一个 Handler.

 

到了这里,有读者肯定会问,这个 Message 中的 Target 是怎么来的呢, 这个 Message 是如何和一个 Handler 关联起来的呢? 

  • 我在这里解释一下,无论我们通过 handler 的post 方法 还是 sendMessage 方法来发送一条消息,在源码中,都会跳转到 Handler 中的如下代码中:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

首先注意这一段代码是 Handler 中的源码 ,看方法名字就知道,是往之前获取的 MessageQueue中插入消息, 然后最重要的就是

 msg.target = this  这句, 通过这句话,这个 Message 就和这个Handler关联起来了,然后在 loop 中就会把这个 message 发送给这个 handler 进行处理!!!


                                                                     总结

 

  1. 通过上面对源码的分析,我们知道要让Handler机制能够正常工作,必须要有一个looper,然后Handler会的往这个looper的MessageQueue中发送消息,之后再调用loop不停的去MessageQueue中取出消息发送给和这个Message关联的Handler进行处理.
  2. 比如我们在线程中调用了 Looper.prepare 那么这个 Looper 就会和该线程关联,然后我们在其他线程中调用 Handler 发送消息, 这个消息自然会被插入到创建 Handler 的那个线程的 MessageQueue 中 ,然后再让 Handler 进行处理 ,这样就实现了 Android中的线程通信原理.
  3. 同时我们也可以从 java 引用的角度来思考 Handler 机制是如何实现的:
  •  第一步 :  Lopper.prepare() 会导致一个线程的 ThreadLocalMap 中持有了一个 Lopper 的引用.
  •  第二步  : new Handler() 导致 Handler 会持有当前运行这句话的那个线程的 Looper 的引用, 然后当我们调用 handler 发送 message 时, 每一条 message 都会持有 handler 的引用(即 message 的 target 成员变量)
  •  第三步 :  Lopper.loop() 会从从当前正在运行这句话的线程中获取 Lopper 引用, 然后进入死循环获取 Looper 中的 message, 然后调用 message.target.dispatchMessage() 最关键的就是这一步,无论这个 Handle r在哪个线程创建,关键的是当前正在运行这句话的线程获取了 handler 的引用, 从而通过 handler 的引用调用了 handler 的成员方法, 从而实现了把 Message 发送到了目标线程.

这里顺便说一下为什么在子线程中创建一个 Handler 而没有调用 Looper.prepare 会报错的原因,其实很简单,就是源码中的一段:

       mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }

 如果有误,欢迎评论指导,谢谢!!

相关标签: Handler机制