handler的作用

  1. 与前端的ajax有异曲同工之妙;
  2. 使得Android开发难度大大降低;
  3. 几乎看不到线程死锁的问题。

线程通信开发中存在的问题?

线程间如何通讯?

Handler 通信实际采用的是内存共享方案

为什么线程间通信不会干扰?

内存管理设计思路优秀

为什么wait和notify用武之地不大?

handler已经将需要的这部分功能进行了Linux层的封装

Handle消息机制模型

20200629140531178png
  • Handler通过sendMessage()发送消息,然后通过enqueueMessage将消息添加到消息队列MessageQueue中;
  • Looper负责去遍历消息队列并且将队列中消息分发给对应的Handler去处理;
  • HandlerhandleMessage方法中处理该消息,这就完成了一个消息的发送和处理过程。

从图中可以看到参与消息处理有四个对象,它们分别是 Handler,Message, MessageQueueLooper

Handler源码分析

源码基于API 30

ThreadLoacal

ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定的线程中可以获取到存储数据,对于其他线程来说则无法获取到数据。

handler

  1. 如何实现线程间跨越的?

  2. Handler是如何管理那块共享的内存的?

启动App时,创建全局唯一的Looper对象和全局唯一的MessageQueue对象

main方法中调用Looper.prepareMainLooper()创建Looper对象和MessageQueue对象,并将其绑定到一起。然后调用Looper.loop()循环遍历取出消息并处理。

public static void main(String[] args) {
    // ...
    Looper.prepareMainLooper();
    // ...
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    // ...
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}
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));
}
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

Activity中创建handler

Handler的构造方法中,新版本中创建一个Handler对象时需要传递Looper对象,在使用的过程中会调用Looper.getMainLooper()获取Looper对象。这就避免了无法在子线程中创建Handler实例的问题。

public Handler(@NonNull Looper looper) {
   this(looper, null, false);
}
public Handler(@NonNull Looper looper, @Nullable Callback callback) {
   this(looper, callback, false);
}
/*
 * @hide
 *  此方法源码隐藏,不对外提供调用
 */
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
   mLooper = looper;
   mQueue = looper.mQueue;
   mCallback = callback;
   mAsynchronous = async;
}

通过handler发送消息

通过handler发送消息时,回调用sendXXX()系列方法,如sendMessage(),通过方法调用跟踪发现,最终会调用到enqueuMessage()将消息压入到MessageQueue中。

public final boolean sendMessage(@NonNull Message msg) {
   return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
   MessageQueue queue = mQueue;
   if (queue == null) {
       RuntimeException e = new RuntimeException(
           this + " sendMessageAtTime() called with no mQueue");
       Log.w("Looper", e.getMessage(), e);
       return false;
    }
   return enqueueMessage(queue, msg, uptimeMillis);
}
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);
}

消息处理

在对Looper创建时,了解到ActivityThread#main()中会调用Looper.loop()处理消息。

// Looper.java
public static void loop() {
   // 从sThreadLocal中获取Looper对象
   final Looper me = myLooper();
   // looper对象不能为空
   if (me == null) {
       throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
   }
   // ...
   // 从looper中获取MessageQueue
   final MessageQueue queue = me.mQueue;
   // ...
   for (;;) {
       // 从messageQueue中不断的获取消息,如果没有消息则堵塞,等待下一条消息将其唤醒
       Message msg = queue.next(); // might block
       if (msg == null) {
            // No message indicates that the message queue is quitting.
           return;
       }
       // ...
       try {
           // msg.target == handler
           // 获取到消息后,回调用handler的dispatchMessage()进行消息分发处理
           msg.target.dispatchMessage(msg);
       } 
       // ...
       // 最后将msg进行回收
       msg.recycleUnchecked();
   }
}
// Handler.java
public void dispatchMessage(@NonNull Message msg) {
   if (msg.callback != null) {
       handleCallback(msg);
   } else {
       if (mCallback != null) {
           if (mCallback.handleMessage(msg)) {
                return;
           }
        }
    handleMessage(msg);
   }
}

dispatchMessage中可以看出处理消息的优先级是 1. msg.callback 2. handler.callback 3. handleMessage。

消息阻塞和延时

Looper的阻塞主要是靠MessageQueue来实现,在MessageQueue#next()时进行阻塞,在MessageQueue#enqueueMessage()进行唤醒。主要是依赖navtive层的epoll机制进行的。

@UnsupportedAppUsage
Message next() {
    // Return here if the message loop has already quit and been disposed.
    // This can happen if the application tries to restart a looper after quit
    // which is not supported.
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }

    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }

        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                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 {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }
        }
        // Reset the idle handler count to 0 so we do not run them again.
        pendingIdleHandlerCount = 0;

        // While calling an idle handler, a new message could have been delivered
        // so go back and look again for a pending message without waiting.
        nextPollTimeoutMillis = 0;
    }
}

阻塞和延时,主要是next()nativePollOnce(ptr, nextPollTimeoutMillis)调用 naive方法操作管道,由nextPollTimeoutMillis决定是否需要阻塞。 nextPollTimeoutMillis为0的时表示不阻塞,为 -1的时候表示一直阻塞直到被唤醒,其他时间表示延时。

唤醒

消息处理的唤醒是在MessageQueue#enqueueMessage()中进行的。

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }

    synchronized (this) {
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        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没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。

这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

延时入队列

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;

此处for死循环推出情况分两种

第一种:p==null表示对象池中已经运行到了最后一个,无需再循环。

第二种:碰到下一个消息when小于前一个,立马退出循环(不管对象池中所有message是否遍历完),进行从新排序。

常见问题分析

  • 为什么不能在子线程中更新UI,根本原因是什么?
// ViewRootImpl.java
void checkThread() {
   if (mThread != Thread.currentThread()) {
       throw new CalledFromWrongThreadException(
         "Only the original thread that created a view hierarchy can touch its views.");
   }
}

mThread是UI线程,这里会检查当前线程是不是UI线程。那么为什么onCreate里面没有进行这个检查呢。这个问题原因出现在Activity的生命周期中,在onCreate方法中,UI处于创建过程,对用户来说界面还不可视,直到onStart方法后界面可视了,再到onResume方法后界面可以交互。从某种程度来讲,在onCreate方法中不能算是更新UI,只能说是配置UI,或者是设置UI的属性。这个时候不会调用到ViewRootImpl.checkThread(),因为ViewRootImpl没被创建。而在onResume方法后,ViewRootImpl才被创建。这个时候去交互界面才算是更新UI。

setContentView只是建立了View树,并没有进行渲染工作(其实真正的渲染工作是在onResume之后)。也正是建立了View树,因此我们可以通过findViewById()来获取到View对象,但是由于并没有进行渲染视图的工作,也就是没有执行ViewRootImpl.performTransversal。同样View中也不会执行onMeasure(),如果在onResume()方法里直接获取View.getHeight()/View.getWidth()得到的结果总是0。

  • 为什么主线程用Looper死循环不会引发ANR异常?

简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制。

  • 为什么Handler构造方法里面的Looper不是直接new?

如果在Handler构造方法里面new Looper,怕是无法保证保证Looper唯一,只有用Looper.prepare()才能保证唯一性。

  • MessageQueue为什么要放在Looper私有构造方法初始化?

因为一个线程只绑定一个Looper,所以在Looper构造方法里面初始化就可以保证mQueue也是唯一的Thread对应一个Looper 对应一个 mQueue。

  • Handler.post的逻辑在哪个线程执行的,是由Looper所在线程还是Handler所在线程决定的?

由Looper所在线程决定的。逻辑是在Looper.loop()方法中,从MsgQueue中拿出msg,并且执行其逻辑,这是在Looper中执行的,因此有Looper所在线程决定。

  • MessageQueue.next()会因为发现了延迟消息,而进行阻塞。那么为什么后面加入的非延迟消息没有被阻塞呢?

MessageQueue 唤醒机制

  • Handler的dispatchMessage()分发消息的处理流程?

Msg.callback 在mHandler.post()中使用, mCallback在new Handler是通过接口回调