Android 复盘——帮你完全了解消息机制

# 1. 什么是消息机制

说到消息机制,做为一名 Android 开发者必定先想到的是 Handler。Handler 就是 Android 消息机制的上层接口,咱们可用经过 Handler 轻松的在不一样的线程中切换任务,但 Handler 的实现还有两个很重要的概念 MessageQueueLooperjava

MessageQueue 的翻译是消息队列,它的内部采用了单链表的结构存储 Handler 对象发送的消息。android

Looper 的做用是不断地查询 MessageQueue 中是否有消息,若是 Looper 发现 MessageQueue 中存入了新的消息,它就会去处理这条消息,若是没有新消息,Looper 就会以无限循环的方式去查询 MessageQueue 中是否有新消息。数组

# 2. 为何要有 Handler

## 2.1)官方文档中 Handler 的主要做用

(1)安排未来某个时间点执行的 MessageRunnables; (2)在不一样于当前的线程上执行的操做;安全

## 2.2)Handler 被用来作的最多的一件事就是更新主线程的 UI。

在 Android 开发中,默认子线程是不能够更新 UI 的,这一点能够从 View 的最高层级 ViewRootImpl 类中找到答案bash

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
    }
}
复制代码

ViewRootImpl 类中的 checkThread 方法会在更新 UI 前被执行,若是当前线程不是主线程,就会抛出 Only the original thread that created a view hierarchy can touch its views. 的异常app

## 2.3)那么 Android 为何要设计为只能在主线程中更新 UI 呢?

  • Android 在子线程中更新 UI 是不安全的,若是多个子线程同时修改一个控件的数据,后果是不可控的
  • 若是给 UI 更新机制加锁,会下降 UI 的访问效率,而且可能阻塞某些线程的执行

# 3. Handler 的用法

## 3.1)在主线程中建立 Handler

一般,咱们在主线程中建立 Handler 的写法以下:异步

private Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};
复制代码

但这样写,系统会这样提示:ide

This Handler class should be static or leaks might occur (anonymous android.os.Handler)
这个Handler类应该是静态的,不然可能会发生泄漏
复制代码

出现这个警告但缘由是,Handler 在 Activity 中做为一个匿名内部类来定义,它的内部持有来 Activity 的实例。当 Activity 被用户关闭时,由于 Handler 持有了 Activity 的引用,就形成了 Activity 没法被回收,从而致使了内存泄漏。oop

所以,在这里推荐一种更加安全的写法:post

private static class MyHandler extends Handler{
    private WeakReference<Activity> weakReference;
    public MyHandler(Activity activity){
        weakReference = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what){
                case 0:                     
                  Toast.makeText(weakReference.get(),Thread.currentThread().getName(),Toast.LENGTH_SHORT).show();
                  break;
            }
    }
}

private MyHandler handler = new MyHandler(this);
复制代码

经过静态内部类的方式实现一个 Handler,此时内部类并不持有外部类对象的应用,须要在内部类的构造方法内增长一个外部类(Activity)的弱应用。这样,即便 Activity 被关闭,Activity 也能顺利被回收。

onCreate() 中的代码以下:

btn_0 = findViewById(R.id.btn_0);
btn_0.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        new Thread(){
            @Override
            public void run() {
                super.run();
                Message message = Message.obtain();
                message.what = 0;
                handler.sendMessage(message);
            }
        }.start();
    }
});
复制代码

这时候点击按钮的运行效果以下:

运行效果

## 3.2)在子线程中建立 Handler

在官方文档中 Handler 的主要做用是在不一样于当前线程的线程中执行操做,那么如何用 Handler 解决两个子线程之间的通讯呢?

请看代码:

btn_1 = findViewById(R.id.btn_1);
btn_1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        new Thread(){
            @Override
            public void run() {
                super.run();
                Looper.prepare();
                handler = new MyHandler(MainActivity.this);
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Looper.loop();
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                super.run();
                Message message = Message.obtain();
                message.what = 0;
                handler.sendMessage(message);
            }
        }.start();
     }
});
复制代码

此时点击按钮:

运行效果

可见当前的处理线程已经变成了子线程。

# 4. Handler 工做原理

若是细心的观察代码,能够看到在子线程中建立 Handler 的时候调用了 Looper.prepare()Looper.loop() 两个方法。这两句代码有什么用呢?

咱们暂时能够把 Looper 理解为消息的管理者,它负责从 MessageQueue 中提取出消息,传递给 Handler 进行处理,每个 Handler 都必需要有一个 Looper,在 Handler 建立的时候,它会自动使用当前线程的 Looper,而 Looper.prepare() 的做用就是为当前线程准备一个 Looper,Looper.loop() 的做用是开始查找当前 MessageQueue 中是否有了新的消息。

这就是 Handler 工做的第一步 :

## 4.1)采用当前线程的 Looper 建立 Handler

由于这里主要讲 Handler 的工做流程,建立 Looper 的具体过程放到文章的下面讲解。咱们只要知道 Looper.prepare() 为当前的线程建立了一个 Looper 对象便可。

可是,在主线程中建立 Handler 的时候,咱们并无看到 Looper.prepare() 的执行,这是由于在 UI 线程,即 ActivityThread 的建立过程当中,Looper 已经被建立好了。

咱们能够在 ActivityThread 的 main() 方法中看到这样一句代码:

Looper.prepareMainLooper();
复制代码

这个方法内部也调用了 Looper.prepare() 为 UI 线程建立了一个 Looper。

## 4.2)经过 Handler 的 sendMessageAtTime() 方法发送 Message

为何是 sendMessageAtTime?不是还有 sendMessage()sendEmptyMessage()sendEmptyMessageDelayed()sendEmptyMessageAtTime()sendMessageDelayed() 这么多方法吗?

经过阅读这些方法的源码能够发现,这些方法最终调用的都是 sendMessageAtTime()

其次还有 post()postAtTime()postDelayed() 方法最终调用的也都是 sendMessageAtTime() 方法,只是多了一步调用 getPostMessage(Runnable r, Object token) 将 Runnable 封装为一个 Message 对象的 callback 里。

public final boolean post(Runnable r){
   return  sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}
复制代码

那么 sendMessageAtTime() 里的具体操做是什么呢?咱们去源码里一探究竟

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    // 先获取当前 Handler 中的 MessageQueue,mQueue 在 Looper 的构造方法中进行初始化。
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    // queue 不为空,则执行 Handler.java 里的另外一个 enqueueMessage() 方法
    return enqueueMessage(queue, msg, uptimeMillis);
}

    
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // 指定 msg 的 Target 对象为当前的 Handler
    msg.target = this;
    if (mAsynchronous) {
       msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码

Handler 中的 enqueueMessage() ,最终会调用 MessageQueue.java 中的 enqueueMessage() 方法。

以后,Message 对象最终传递到 MessageQueue 即消息队列里中,在消息队列里的具体处理逻辑在文章的 MessageQueue 工做原理 部分会具体解释。

## 4.3)Looper 处理消息后调用 Handler 的 dispatchMessage() 方法

在第二步将消息插入消息队列后,Looper 就开始遍历消息队列,找到新的消息,再通知 Handler 去执行这条消息,调用的就是 Handler 的 dispatchMessage() 方法。

public void dispatchMessage(Message msg) {
   // msg 的 callback 对象就是一个 Runnable
   if (msg.callback != null) {
        handleCallback(msg);
    } else {
        // 检查 mCallback 是否为空,不为空就执行它内部定义的 handleMessage() 方法
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 若是 mCallback 为空,就执行在实例化 Handler 过程当中咱们本身定义的 handleMessage() 方法中的内容
        handleMessage(msg);
    }
}
复制代码

dispatchMessage() 方法首先会检查 Message 的 Callback 对象是否为空,callback 就是经过 post() 方法传递的 Runnable 对象,若是 callback 不为空,就去执行 handleCallback() 方法。

handleCallback() 方法的实现也很简单,它在内部执行了 Runnable 的 run() 方法

private static void handleCallback(Message message) {
    message.callback.run();
}
复制代码

若是 callback 对象为空,就检查 mCallback 是否为空,不为空就执行它的定义的 handleMessage() 方法,若没有 mCallback,最终将直接执行咱们在继承 Handler 时本身定义的 handleMessage() 方法中的代码。

Callback 是 Handler 中定义的的一个接口,它的代码以下:

/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. */
public interface Callback {
    /** * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */
    public boolean handleMessage(Message msg);
}
复制代码

若是使用 Callback 接口的话,咱们能够直接实例化一个 Handler 而不用去实现一个 Handler 的子类,

private Handler mHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        return false;
    }
});
复制代码

# 5. MessageQueue 工做原理

咱们从上一部分的 MessageQueue.java 中的 enqueueMessage() 方法开始入手。

## 5.1)enqueueMessage()

代码量有点多,要耐心看哦!

boolean enqueueMessage(Message msg, long when) {
    // 检查当前 msg 的 target 是否为空
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    // msg 若是正在被执行,就抛出异常
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        // 在 quit() 方法中,mQuitting 会被设置为 true
        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 正在执行
        msg.markInUse();
        // 设置 msg 的 when 为传进来的 when 参数,when 是 Message 想要被执行的时间
        msg.when = when;
        // 获得当前消息队列的头部消息
        Message p = mMessages;
        boolean needWake;
        // 当前消息队列为空,新消息的触发时间为 0,或者新消息的触发时间早于消息中第一条消息的触发时间
        // 则将新消息插入到队列的头部,做为当前消息队列的第一条消息
        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 {
            // 将新消息插入到当前消息队列当中,(不是头部)
            // 一般咱们没必要唤醒事件队列,
            // 除非队列头部有消息障碍,而且消息是队列中最先的异步消息。
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            // 开始循环便利消息队列,比较新消息和队列中消息的 when(触发事件)的值,将新消息插入到适当位置
            for (;;) {
                // 循环第一次遍历时,将当前队列中的头部消息赋值给 prev
                prev = p;
                // p 指向队列中的第二个消息
                p = p.next;
                // 若是下一个消息为空,或者新消息的触发时间早于下一个消息,找到了要插入的位置,退出循环
                if (p == null || when < p.when) {
                    break;
                }
                // needWake 为 true,而且 下一条消息是异步的,则不须要唤醒。
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            // 将新消息插入到 p 以前,头消息以后。
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // 若是须要唤醒,调用 nativeWake 方法去唤醒
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}
复制代码

执行完 enqueueMassage 方法,咱们新发送的 Message 就成功的插入了消息队列当中。 可是除了插入新消息,咱们还须要从消息队列中读取消息,这又要怎么作呢?

## 5.2)next()

Message next() {
    // 若是消息循环已退出,而且被丢弃,则返回空。
    // 这个将在应用重启一个 looper 时发生
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }

    // 记录空闲时处理的 IdlerHandler 数量,只在第一次迭代时为 -1
    // IdleHandler 只在队列为空 或者 是头部消息时执行
    int pendingIdleHandlerCount = -1;
    // native 层使用的变量,设置的阻塞超时时长,0 为不阻塞,-1 为阻塞
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }

        nativePollOnce(ptr, nextPollTimeoutMillis);
  
        // 尝试检索下一条消息。 若是找到则返回。
        synchronized (this) {
            // 获取系统从开机到如今到时间
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            // 将队列中到头部消息赋值给 msg
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // msg 不为空,可是这个 msg 没有 handler,则这个 msg 为栅栏
                // 开始遍历,指到获取第一个异步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                // 若是当前时间不到 msg 的触发时间,则计算时间差,设置阻塞超时时长
                if (now < msg.when) {
                    // Next message is not ready. Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 当前时间到了 msg 的触发时间,则获取消息并返回
                    mBlocked = false;
                    // 若是当前的 msg 不是头部消息,则上一条消息的 next 指向 msg 的 next
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        // 当前 msg 为头部消息,则将下一个 msg 设置为头部消息
                        mMessages = msg.next;
                    }
                    // msg 的下一个 Message 对象置空,表示从消息队列中取出来了这条 msg
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    // 标记 msg 正在使用
                    msg.markInUse();
                    return msg;
                }
            } else {
                // 若是没有消息,则设置阻塞时长为 -1,直到被唤醒
                nextPollTimeoutMillis = -1;
            }

            // 全部的消息都被处理后,判断是否退出,并返回 null。
            if (mQuitting) {
                dispose();
                return null;
            }

            // If first time idle, then get the number of idlers to run.
            // Idle handles only run if the queue is empty or if the first message
            // in the queue (possibly a barrier) is due to be handled in the future.
            // 第一次循环时,消息队列为空,或 当前时间未到消息的触发时间,获取 IdleHandler 的数量
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
  
            // pendingIdleHandlerCount 的数量为 0 时,线程会继续堵塞
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run. Loop and wait some more.
                mBlocked = true;
                continue;
            }

            // 判断当前空闲时处理任务的handler是不是为空,若是为空,就实例化出新的对象
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // 运行 IdleHandler,只有第一次循环时才会运行
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            // 释放 IdleHandler 的引用
            mPendingIdleHandlers[i] = null;

            boolean keep = false;
            try {
                // 执行 IdleHandler 的方法
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }

            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }

        // 重置 IdleHandler 的数量为 0,确保不会重复运行它们
        pendingIdleHandlerCount = 0;

        // 在执行 IdleHandler 时,一个新的消息可能插入或消息队列中的消息到了触发时间
        // 因此将 nextPollTimeoutMillis 设为 0,表示不须要阻塞,从新检查消息队列。
        nextPollTimeoutMillis = 0;
    }
}
复制代码

至此,MessageQueue 的两个最重要的方法已经分析完了,下面来看 Looper 如何循环地从消息队列中取出消息。

# 6. Looper 工做原理

在讲 Looper 以前,须要先理解 ThreadLocal 的工做原理

## 6.1)ThreadLocal 的工做原理

ThreadLocal 是一个线程内存储数据的类,当不一样的线程去访问同一个 ThreadLocal 对象时,得到的值都是不同的,下面用一段代码来证实

private ThreadLocal<String> mThreadLocal = new ThreadLocal<>();

btn_1 = findViewById(R.id.btn_1);
btn_1.setOnClickListener(new View.OnClickListener() {
	@Override
	public void onClick(View v) {
		new Thread(){
			@Override
            public void run() {
				super.run();
				mThreadLocal.set("Thread_A");
				Log.d("ThreadLocalValue",mThreadLocal.get());
			}
        }.start();
		new Thread(){
			@Override
            public void run() {
	            super.run();
                mThreadLocal.set("Thread_B");
                Log.d("ThreadLocalValue",mThreadLocal.get());
			}
		}.start();        
    }
);
复制代码

我在两个线程中分别存入在 mThreadLocal 中存入了不一样的值,而后在控制台输出它们的内容

不一样线程访问 ThreadLocal 对象

可见不一样线程访问同一个 ThreadLocal 对象获得的值也是不同的。

ThreadLocal 实现这种特性的缘由也很简单,下面来看它内部的 set 方法:

public void set(T value) {
    // 获取当前线程 t
    Thread t = Thread.currentThread();
    // 根据当前线程 t,获取当前线程的 ThreadLocalMap 对象
    ThreadLocalMap map = getMap(t);
    if (map != null)
        // map 不为空,调用 ThreadLocalMap 的 set() 方法。
        map.set(this, value);
    else
        // map 为空,则为当前线程建立一个新的 ThreadLocalMap 对象
        createMap(t, value);
}
复制代码

在 set 方法中,先获取当前线程,而后获取当前线程的 ThreadLocalMap 对象。getMap() 的 和 createMap() 的实现以下:

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
复制代码

那么 ThreadLocalMap 又是什么呢,这里是它的一部分源码:

static class ThreadLocalMap {
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }

    // 初始的 table 容量
    private static final int INITIAL_CAPACITY = 16;
  
    // Entry 数组用于存储数据
    private Entry[] table;

    // table 的大小
    private int size = 0;

    // 负载因子,用于扩容
    private int threshold; // Default to 0

    // 设置负载因子为固然容量大小的 2 / 3 
    private void setThreshold(int len) {
        threshold = len * 2 / 3;
    }
  
    // 初始化 Entry 数组
    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
        table = new Entry[INITIAL_CAPACITY];
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        table[i] = new Entry(firstKey, firstValue);
        size = 1;
        setThreshold(INITIAL_CAPACITY);
    }
}
复制代码

能够将 ThreadLocalMap 看成一个哈希表,它的内部用 Entry 存储相应的数据。

在 Thread 的属性中有 ThreadLocal.ThreadLocalMap threadLocals = null;,因此每个线程内部,都持有一个 ThreadLocalMap 对象,系统才能够经过 getMap() 方法获取当前线程的 ThreadLocalMap 对象。

在 ThreadLocal 中调用 set 方法,实际上会调用 ThreadLocalMap 中的 set 方法,源码以下:

// ThreadLocalMap 的 set 方法
private void set(ThreadLocal<?> key, Object value) {

	// We don't use a fast path as with get() because it is at
	// least as common to use set() to create new entries as
	// it is to replace existing ones, in which case, a fast
	// path would fail more often than not.

    // 首先获取当前 ThreadLocal 对象的 table 属性,table 一个 Entry 的数组
    // Entry 至关于一个 HashMap,存储了当前 ThreadLocal 对象和 Object 类型的 value 对象
    Entry[] tab = table;
    int len = tab.length;
    // 计算出存储的位置
    int i = key.threadLocalHashCode & (len-1);

    // 遍历 tab
    for (Entry e = tab[i];
    	e != null;
        e = tab[i = nextIndex(i, len)]) {
        ThreadLocal<?> k = e.get();
        // 若是 tab 中已经存在了相同的 key 值,就覆盖它原有的 value
        if (k == key) {
        	e.value = value;
            return;
        }
        // 若是 当前 entrt 的 key 为 null,调用 replaceStaleEntry 方法清楚全部 key 为 null 的数据
    	if (k == null) {
        	replaceStaleEntry(key, value, i);
            return;
        }
    }
        // 都不知足,就新建一个 Entry 对象
	tab[i] = new Entry(key, value);
    int sz = ++size;
    // ThreadLocalMap 的容量到达阀值后扩容
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
    	rehash();
}
复制代码

ThreadLocal 中的 get() 方法和 set() 方法同样,都是对 Thread 中对 ThreadLocalMap 进行操做

public T get() {
    // 获取当前线程
    Thread t = Thread.currentThread();
    // 获取当前线程的 ThreadLocalMap 对象
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 获取 ThreadLocalMap 中对应当前线程的 Entry 对象
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            // 将 Entry 对象中的 value 取出来
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

private Entry getEntry(ThreadLocal<?> key) {
    int i = key.threadLocalHashCode & (table.length - 1);
    Entry e = table[i];
    if (e != null && e.get() == key)
        return e;
    else
        return getEntryAfterMiss(key, i, e);
}
复制代码

## 6.2)Looper 中的 prepare() 方法

那么 ThreadLocal 和 Looper 有什么关系呢?咱们知道每个线程都有本身的 Looper,Looper 的做用域就是当前的线程,Android 系统中便经过 ThreadLocal 对象来存储不一样线程中的 Looper。

Looper 中 prepare() 方法为当前线程建立一个 Looper 对象,咱们看一下它的实现:

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");
    }
    // 将 Looper 对象保存到当前线程的 ThreadLocalMap 当中
    sThreadLocal.set(new Looper(quitAllowed));
}
复制代码

这里再看一下 Looper 的构造方法

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

能够看到在一个 Looper 中建立了一个 MessageQueue,这里咱们就能够搞清楚 Handler、Looper 和 MessageQueue 的对应关系了:

每一个线程都有一个 Looper 对象,在 Looper 对象的初始化过程当中,会为当前线程建立一个 MessageQueue,而一个线程中能够有多个 Handler。

## 6.3)Looper 中的 loop() 方法:

prepare() 调用后,就是调用 loop() 方法:

/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */
public static void loop() {
    // 经过 Thread Local 获取当前线程的 Looper
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    // 获取当前 Looper 对象的 MessageQueue
    final MessageQueue queue = me.mQueue;

    // 清空远程调用端进程的身份,确保此线程的身份是本地进程的身份,并跟踪该身份令牌
    // 这里主要用于保证消息处理是发生在当前 Looper 所在的线程
    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;
        }
        
        // 用 logging 打印日志,默认为 null,可经过 setMessageLogging() 方法来指定
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        
        // 开始跟踪,并写入跟踪消息,用于 debug 功能
        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }        
        ...
        ...
        try {
            // // 经过 Handler 分发消息
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                // 中止跟踪
                Trace.traceEnd(traceTag);
            }
        }
        
        if (logSlowDispatch) {
            showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
        }

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // 确保在分发消息的过程当中线程的身份没有改变
        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() 方法就是不停的遍历消息队列中的消息,当发现有新的消息时,便调用 Handler 的 dispatchMessage() 方法。

## 6.4)getMainLooper()

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}
  /** * Returns the application's main looper, which lives in the main thread of the application. */
public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}
复制代码

getMainLooper() 方法用于返回当前 UI 线程的 Looper,UI 线程的 Looper 在 ActivityThread 的创建时经过调用 prepareMainLooper() 方法建立。

## 6.5)quit() 和 quitSafely()

在子线程中,若是手动为其建立了Looper,那么在全部消息处理完成以后应该调用 quit() 方法终止消息循环,否则 Looper 就会一直处于等待状态。

public void quitSafely() {
    mQueue.quit(true);
}

public void quit() {
    mQueue.quit(false);
}
复制代码

能够看到这两个方法都调用了 MessageQueue 中都 quit(boolean safe) 方法,quitSafely 的参数为 true,quit 的参数为 false。

void quit(boolean safe) {
    // 主线程不退出消息循环
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }
    synchronized (this) {
        // 若是已经退出了,直接 return
        if (mQuitting) {
            return;
        }

        // 标记为已经退出
        mQuitting = true;
        // 若是 safe 的值为 true,执行完当前的消息后退出消息循环
        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            // 直接退出消息循环
            removeAllMessagesLocked();
        }
        // We can assume mPtr != 0 because mQuitting was previously false.
        nativeWake(mPtr);
    }
}
复制代码

quitSafely() 会等待当前消息执行完毕后退出消息循环,而 quit() 方法会直接退出消息循环。

private void removeAllMessagesLocked() {
    // 获取当前 MessageQueue 的头部消息
    Message p = mMessages;
    while (p != null) {
        // 循环遍历全部的 Message
        Message n = p.next;
        // 回收消息,并把消息放入消息池
        p.recycleUnchecked();
        p = n;
    }
    // 将头部消息置为空
    mMessages = null;
}

private void removeAllFutureMessagesLocked() {
    // 获取系统从开机到如今到时间
    final long now = SystemClock.uptimeMillis();
    // 将当前的头部消息赋值给 p
    Message p = mMessages;
    if (p != null) {
        if (p.when > now) {
            // 若是当前头部消息将要执行的时间大于系统开机到如今的时间,则执行 removeAllMessagesLocked() 方法
            // 清空 MessageQueue 队列
            removeAllMessagesLocked();
        } else {
            Message n;
            // 遍历当前的 MessageQueue,直到某个消息的执行时间小于 now 值(即这个消息正在执行)
            // 将这个消息的 next 赋值为 null
            for (;;) {
                n = p.next;
                if (n == null) {
                    return;
                }
                if (n.when > now) {
                    break;
                }
                p = n;
            }
            p.next = null;
            // 回收不会被执行的 Message
            do {
                p = n;
                n = p.next;
                p.recycleUnchecked();
            } while (n != null);
        }
    }
}
复制代码

终于讲完了,但愿你们能经过个人文章,完全理解 Handler 的机制,但个人能力有限,若是存在错误的地方,还请指出。

零碎的东西不少,为了方便你们记忆,我把上面的内容作成了思惟导图,须要的朋友能够保存下来,偶尔看一下,帮助本身记忆。

Android 消息机制

欢迎关注本文做者:

扫码关注并回复「干货」,获取我整理的千G Android、iOS、JavaWeb、大数据、人工智能等学习资源。

相关文章
相关标签/搜索