源码学习《8》Handler,Message,MessageQueue,Looper,ThreadLocal 通讯机制

关于Handler通讯之前也看了好几遍,记忆慢慢模糊了,希望通过这次详细的整理能记忆的更久。本篇涉及的类比较多,为了更好的记忆,我采用了先整体在局部的叙事方式。先整体把流程梳理,再详细说明流程中涉及到的某些类。

首先通过以往的章节源码学习,我们对一下流程应该都熟悉了

  1. init 进程,zygote进程,systemserver进程的启动
  2. PMS启动后解析manifest.xml文件数据到application
  3. AMS启动launcher,systemui
  4. AMS 创建应用进程
  5. LoadedApk 资源文件的加载(换肤)
  6. ClassLoader 类加载器(热修复)

今天就来学习,AMS创建应用进程之后,发生了什么。从主线程引入Handler等的流程。把Handler通讯分两部分

  1. 线程中Looper的创建,开启无线循环从队列中取出消息
  2. handler发送消息到 MessageQueue队列排序

1. 线程中Looper的创建和开启无线循环获取msg

  •     首先我们从ActivityThread main() 进入Looper的创建流程
public static void main(String[] args) {
        // 为 当前 app 创建私有用户存储文件夹
        Environment.initForCurrentUser();
        // 为当前线程创建looper
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        // 开启无线循环,获取消息队列中的消息       
        Looper.loop();  
    }

通过上述代码Looper.prepareMainLooper()调用到了Looper中。

public static void prepareMainLooper() {
        // 创建looper
        prepare(false);
        synchronized (Looper.class) {
            // 判断是否已经创建过
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            // 赋值到全局变量
            sMainLooper = myLooper();
        }
    }

通过prepareMainLooper进入 prepare()方法创建looper对象

 private static void prepare(boolean quitAllowed) {
        // 判断当前线程是否已经创建过了 looper 对象
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 未创建looper对象
        sThreadLocal.set(new Looper(quitAllowed));
    }

根据代码很容易发现looper对象创建过程中,代码转入到了 ThreadLocal 对象,那ThreadLocal又是什么呢?

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

ThreadLocal<Looper> 是Looper类中的 static 的全局变量,说明sThreadLocal 是属于类的,并且被保存在老年代内存中。 所以不管有多少个Looper对象对应的ThreadLocal<Looper>对象只有一个,那怎么可能维护不同线程不同的looper对象的呢,其实ThreadLocal<Looper>中维护了一个ThreadLocalMap来保存Looper对象的。先来看看ThreadLocal<Looper>的get() set() 方法。

 public T get() {
        // 获取当前线程
        Thread t = Thread.currentThread();
         // 根据线程获取 map 对象
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        // 为null 就创建一个新的
        return setInitialValue();
    }

  void createMap(Thread t, T firstValue) {
        // ThreadLOcalMap 是一个线程的全局变量
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

  public void set(T value) {
        Thread t = Thread.currentThread();
        // 根据线程取map
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            // 没有创建
            createMap(t, value);
    }

对于 ThreadLocalMap 原理根据这篇文章学习 https://blog.csdn.net/weixin_41344042/article/details/83024039
通过以上流程发现 我们创建的looper对象是被保存到Thread中的ThreadLocalMap t 的全局变量中。

  • Looper的无线循环 
 // 开启无线循环,获取消息队列中的消息       
        Looper.loop();  
根据上述代码知道,在looperprepare 之后就调用了 loop() 方法开启循环,具体怎么循环的,接下来看代码
 public static void loop() {
        // 获取当前线程的looper对象
        final Looper me = myLooper();
        // 获取 Message 队列
        final MessageQueue queue = me.mQueue;
         // 死循环 获取message
        for (;;) {
            // Message 是个单向列表
            Message msg = queue.next(); 
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                // 通过转发给 handler 持行当前消息 
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } 
    }
从loop方法中看到首先是获取当前loop 的MessageQueue队列,从队列中取出地一个message,然后发出去。这个过程涉及到了MessageQueue的 next()和handler 的 dispatchMessage()方法。
Message next() {
        // 指针
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        // 下次执行 nativePollOnce(ptr, nextPollTimeoutMillis); 的时间
        int nextPollTimeoutMillis = 0;
        for (;;) {
            // 持行延迟消息
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                // 获取当前时间
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                // 获取第一个 handler 不为null的消息
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    // 当前时间小于 msg的发送时间,就是消息还没到持行的时间点,
                    if (now < msg.when) {
                        // 下一次持行nativePollOnce(ptr, nextPollTimeoutMillis);的时间
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 获得一个消息
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        // 把发送出去的消息,从队列中移除
                        msg.next = null;
                        // 返回 msg 给handler
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
            nextPollTimeoutMillis = 0;
        }
    }

Message 消息是一个单向列表,这个列表由 MessageQueue 类来管理,MessageQueue 中next()方法用来获取message这个消息,发给handler 处理。

以上就是线程中Looper的创建和开启无线循环过程。

2. handler发送消息到 MessageQueue队列排序

在 1 中已经引出了 looper 从队列中取出消息的过程,接下来看下消息加入到队列中。 

通过对以上looper.loop() 开启了无线循环获取message,这时message队列中还没有msg,我们调用handler方法向队列中发送msg。

调用 handler 的发送消息方法都会走到这里

  public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        // 当前时间 + 延迟时间           
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

然后走到 MessageQueue 中的 enqueueMessage() 方法。

 boolean enqueueMessage(Message msg, long when) {
        synchronized (this) {
            // 获取当前队列中的 msg的时间
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            // 把消息排序,把传入的消息 放到head头部
            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 (;;) {
                    // 把 head 头赋值给prev
                    prev = p;
                    // 取出 第二个消息
                    p = p.next;
                    // 根据时间 排序,来定义 消息持行的顺序
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                // 把msg插入到head头的后面第一个
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
        }
        return true;
    }

这里就是消息排序,把消息放入到MessageQueue队列中,然后等待looper.loop()去获取消息,然后分发给handler去处理。

发布了119 篇原创文章 · 获赞 140 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/WangRain1/article/details/103579014