探究Handler的运行机制

Handler 作为 Android 开发中重要的消息通讯的工具,在我们日常开发中经常使用到,虽然经藏使用,也是一知半解,所以今天就去看一下 Handler 的源码到底怎么实现的呢?

1 Handler 是什么呢?
众 android 开发者周知, Handler 是我们用来进行线程间通讯的工具。像我们网络请求返回的数据,就会经常使用Handler把数据回传给主线程。然后主线程展示相关数据。

2 为什么要用 Handler 呢?
android 要求耗时的任务要在子线程中进行,不然会造成UI界面的卡顿,而我们的 UI 界面是在主线程。要把子线程的数据显示到主线程则就要用到 handler 了,不然,你在子线程直接刷新UI界面,程序会报错。

3 看下我们平时如何用 Handler 的呢?

public class TestActivity extends AppCompatActivity {

    Handler handler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what){
                case 0x1111:
                    Toast.makeText(this,"收到子线程发来的消息了",Toast.LENGTH_LONG).show();
                    break;
            }
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        new Thread(){
            @Override
            public void run() {
                Message message = Message.obtain();
                message.obj = "这是一个handler消息";
                message.what = 0x1111;
                handler.sendMessage(message);
            }
        };

    }
}

上面代码是我们 Handler 的常用方式,接下来去看下它内部具体是怎么实现的呢?进入源码中去查看;

Handler 的构造方法

(1) 首先咱们看一下创建Handler都去做了哪些工作。

  Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };

调用构造方法:

    public Handler() {
        this(null, false);
    }
  public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) { 
            final Class klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {// 判断Handler 是否为静态修饰
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();// 获取 looper
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;// looper 的 mQueue 值赋值给 Handler 类中 mQueue 
        mCallback = callback;
        mAsynchronous = async;
    }

可以看到 handler 构造的过程中,如果定义 handler 没有用 static 修饰符的话,还会提示你会出现内存溢出的风险(这里和 Handler 持有 Activity 有关);

接下来会去获取到一个 Looper 对象。同时把 Looper 类里面的消息队列 mLooper.mQueue 赋值给了 Handler 类里面的队列常量 mQueue;

先继续查看 Looper 获取情况。

mLooper = Looper.myLooper();

进入到Looper类中。

  public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
static final ThreadLocal sThreadLocal = new ThreadLocal();
private static Looper sMainLooper;  // guarded by Looper.class
final MessageQueue mQueue;

在 Looper 类里面维护了一个 sThreadLocal 的变量,我们获取到的 looper 是从这个变量中取的。

sThreadLocal 实际是一个 ThreadLocal 类,ThreadLocal 是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据。

ThreadLocal

public class ThreadLocal {

    public ThreadLocal() {}

    public T get() {//获取
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);//获取到当前线程的 values
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }

        return (T) values.getAfterMiss(this);
    }

    public void set(T value) { // 存储
        Thread currentThread = Thread.currentThread();// 获取到当前线程
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }

可以看到 ThreadLocal 内部为了类 Values,而存储数据以 key、value的方式存储在了 values 中。
key---ThreadLocal 类,value---将要存储的值。这里看到 values 是和 线程有关系,那么 ThreadLocal 在不同线程中取到的 values 值肯定是不同的。实际上不同的线程对应的也是不同的 ThreadLocal 对象;

ThreadLocal 的与线程之间关系可简单表示为上述的关系;

既然在 Handler 构造过程中通过 ThreadLocal 去取 Looper 实例,那么就一定会有存储 Looper 实例的地方。
我们一般都在主线程去进行 Handler 的构造,那么这里就假定在主线程中的操作;

这里直接定位到 ActivityThread 中,即系统运行的主线程:

    public static void main(String[] args) {
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        Security.addProvider(new AndroidKeyStoreProvider());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("");

        Looper.prepareMainLooper();// 准备主线程的 looper 对象

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        Looper.loop(); // 开启 loop 循环查询消息

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

在 ActivityThread 的 main 方法中, 调用 Looper.prepareMainLooper() 方法去准备主线程的 Looper;

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

   public static void prepare() {// 准备looper
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {// 每个线程只能有一个looper 对象
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

到这里发现了在主线程开始的时候,便初始化了 Looper ,设置对应的 Looper 在 ThreadLocal 中,且每个线程只对应一个 Looper 对象;

ThreadLocal.png

通过上述过程我们知道了,在 Handler 的构造过程中,会通过 ThreadLocal 类去拿到对应线程的 Looper 对象,这里 Looper 对象获取了为后续的发送消息做准备。

另外,Handler 构造过程中同时将 Looper 的 MessageQuene 赋值给了 Handler 的 MessageQuene ;

 mQueue = mLooper.mQueue;// looper 的 mQueue 值赋值给 Handler 类中 mQueue 

MessageQuene

public final class MessageQueue {
    // True if the message queue can be quit.
    private final boolean mQuitAllowed;

    @SuppressWarnings("unused")
    private long mPtr; // used by native code

    Message mMessages;
    private final ArrayList mIdleHandlers = new ArrayList();
    private IdleHandler[] mPendingIdleHandlers;
    private boolean mQuitting;

    // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
    private boolean mBlocked;

    // The next barrier token.
    // Barriers are indicated by messages with a null target whose arg1 field carries the token.
    private int mNextBarrierToken;

    private native static long nativeInit();
    private native static void nativeDestroy(long ptr);
    private native static void nativePollOnce(long ptr, int timeoutMillis);
    private native static void nativeWake(long ptr);
    private native static boolean nativeIsIdling(long ptr);
}

MessageQuene 主要定义了一些常量以及和 c 交互的方法,那么直接去 Looper 中查看使用 MessageQuene 的地方;

在 ActivityThread 的 main 方法中,会调用 Looper 的 loop 方法,直接从这里入手查看 Looper 与 MessageQuene 的关联;

   public static void loop() {
        final Looper me = myLooper();// 获取当前线程的looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue; // 通过 looper 获取到队列

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        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;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);// 分发获取到的 msg 信息

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

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            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 方法中有个无限循环去从 MessageQuene 中取下一个消息。只有取到的消息为 null 的时候才会退出这个无限循环。

查看 MessageQuene 的 next 方法去取消息;

   Message next() {
        ......

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

            nativePollOnce(ptr, nextPollTimeoutMillis); // 调用 native 的方法

            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 (false) Log.v("MessageQueue", "Returning message: " + msg);
                        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;
                }
        ......
        }
    }

这里的 next 方法同样挂起了一个无限循环,那么就有一个疑问,为什么 MessageQuene 开启一个无限循环不会导致程序的卡死崩溃呢?

这里就和 nativePollOnce 这个方法有关了,nativePollOnce 通过底层阻塞住,直到 MessageQuene 中有消息将其唤醒,通过 nativeWake 方法进行唤醒取到消息。实际通过底层的阻塞状态来进行是否读取消息,并不会导致阻塞线程,具体底层不在此文讨论。可自行查看 c 代码;

当 Handler 处有消息进行发送的时候,此时 MessageQuene 的 next 方法会获取到消息 Message ,然后会通过 msg.target.dispatchMessage(msg) 进行分发消息;

Looper.png

到这里,总结下 Handler 初始化之前以及初始化的过程都做了哪些事情:
1、ActivityThread 会准备 Looper,同时通过 ThreadLocal 类将线程和 Looper 绑定后存储在 ThreadLocal中;
2、Looper 中的 MessageQuene 赋值给 Handler 中的 MessageQuene 变量;
3、Activity 初始化的 Looper 开启 loop 方法对 MessageQuene 进行消息循环获取;获取到

Handler 发送来的消息后,通过 dispatchMessage 方法进行分发;

接下来,继续查看 Handler 发送消息的源码;

Handler 发送消息

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public boolean sendMessageAtTime(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);
    }

Handler 通过 sendMessage 发送消息,最终会调用到 enqueueMessage 方法,同时如果有延迟发送,会将延迟时间传入;

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this; // msg 的 taget 指向 此Handler
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

Handler 的 enqueueMessage 方法最终会调用到 MessageQueue 的 enqueueMessage 方法;而 mQueue 在 Handler 的初始化方法中提到过,其实是由 Looper 中的 MessageQueue 赋值的,所以 Handler 中的变量 mQueue 其实是和 Looper 中的变量 mQueue 一样的;

    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("MessageQueue", 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) { // msg 插如队列头部
                // 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 (;;) { // 无限循环 msg 放到 队列的尾部
                    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) { // 是否需要通知 native 唤醒队列
                nativeWake(mPtr);
            }
        }
        return true;
    }

MessageQuene 的 enqueueMessage 方法会去将 msg 插入到双向队列之中,发送的 msg 信息到最后一位,如果需要唤醒队列读取 msg 消息,调用 nativeWake 方法;

在上面 Looper 的 loop 方法中有提到如果 MessageQuene 中没有消息就会底层阻塞住读取消息,而当我们发送消息的时候,nativeWake 则会将阻塞状态转成运行状态,此时 loop 方法读取到 msg 消息;

那么我们就又回到了 dispatchMessage 方法了,而进行分发消息的则是 msg.target,即 Handler 类处理;

dispatchMessage 方法

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

很明显,dispatchMessage 方法会回调到 Handler 重写的 handleMessage 方法;到此整个 Handler 的处理过程形成了闭环;

最后,将Handler、Looper、Message、MessageQuene、ThreadLocal 形成一个闭环,看下整个过程的大致图解:


Handler.png

最后还有个问题,如果是在子线程里面可以创建 Handler 发送和接收消息嘛?


  class LooperThread extends Thread {
         public Handler mHandler;

        public void run() {
              Looper.prepare();

              mHandler = new Handler() {
                  public void handleMessage(Message msg) {
                      // process incoming messages here
                  }
              };
    
             Looper.loop();
        }
  }

上面是Looper 类上面的注释,子线程也可以去用来处理接受消息,主线程里面 ActivityThread 方法会帮助我们去调用 Looper.prepare()方法,从而设置 Looper 实例,而在子线程里面则需要自己进行创建,保证用于 Looper 实例可以去循环取消息了;

你可能感兴趣的:(探究Handler的运行机制)