Android 深入理解之Handler

1.概括

    Handler对于大家来说并不陌生,它是android消息机制的重要组成成员之一,我们在子线程中更新UI中经常会用到它。因为UI操作是一个频繁且线程不安全的操作,所以更新UI需要在主线程中进行。我们可以在子线程中完成耗时操作,然后再通过Handler发送消息就可以在主线程更新UI了。当然,Handler不仅仅可以更新UI,还具有异步消息处理、线程切换调度等功能。Handler的使用也非常的简单:

首先,我们创建一个Handler对象:

Handler mHandler  = new Handler(){
      @Override
      public void handleMessage(Message msg) {
          switch (msg.what){
              case 1:
                   textView.setText("hello handler");
                  break;
          }
      }
  };

这里使用了内部类的方式来定义一个Handler的子类,并重写handleMessage()方法,在该方法中处理收到的消息。

接着我们调用handler的sendMessage()方法来发送一条消息:

Message msg = Message.obtain();
msg.what = 1;
mHandler.sendMessage(msg);

这样,在handleMessage()中就会收到刚才发送的Message,因为what的值等于1所以textView的文本内容会被更新。可以看到,使用Handler来更新UI是非常简单的。那么,这里面的实际流程是怎么样的?稍后我们会通过源码来深入理解。这里先介绍两个类。想必大家都知道,Handler是配合Looper和MessageQueue使用的,那么Looper和MessageQueue是什么?

MessageQueue

    MessageQueue的意思是“消息队列”,其作用是存放Handler发送的消息,虽然是叫Queue,但其内部是以单向链表的结构来存放Message的。通过调用enqueueMessage()可以将Message加入到队列当中,然后调用next()方法可取出”对头”的消息。

Looper

    Looper的作用是对消息进行轮询操作,Looper是Handler和MessageQueue之间的通讯桥梁。线程默认是没有Looper对象的,需要调用Looper.prepare() 来创建Looper对象,然后通过Looper.loop() 轮询MessageQueue中的Message。

OK,这里先大概了解这两个类的作用是什么就行了,下面附上一张网络图片来帮助大家加深理解。
Handler通信流程
大致就是这么一个流程,好了,请退后,我要开始撸源码了。

2.源码分析

    首先看一下Handler的构造方法,Handler的几个重载的构造方法都会间接调用这个构造方法。创建Handler的时候,需要当前线程的Looper对象,前面提到,线程默认是没有Looper对象的,如果没有Looper对象,创建Handler时就会抛出异常,在第5中行可以看到。

 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;
    }

mLooper 是Handler的成员变量,在Handler的构造方法中进行初始化,那我们点进去看Looper.myLooper()

 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

调用的是sThreadLocal的get()方法,sThreadLocal是ThreadLocal对象,它是Looper类的一个静态成员。声明如下:

 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

其作用是为每个线程保存一个独立与其他线程的Looper对象,那么我们来看看Looper.prepare()做了哪些工作。

 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");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

在调用Looper.prepare()时会创建一个Looper对象并放入到sThreadLocal中。到这一步就可以知道为什么线程创建Handler先要调用Looper.prepare()了,如果没用调用sThreadLocal.set()存入Looper对象,那么在调用sThreadLocal.get()就会返回null,在初始化Handler的时候就会抛出异常。再来看看Looper的构造方法:

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

可以看到,MessageQueue对象是在Looper中创建,回到Handler的构造方法,看到第8行的mQueue = mLooper.mQueue。这就说明Handler中的mQueue实际上是Looper对象的成员。这里我们先做一个总结:

1. 创建Handler必须要有该线程的Looper对象,否则会抛出异常
2. 在Looper构造方法中会创建MessageQueue对象

理清了这三者的关系之后,我们看一下Handler的SendMessage(Message msg)方法:

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

调用的是sendMessageDelayed(),点击去看:

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

点进去看sendMessageAtTime()方法:

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);
  }

sendMessageAtTime()接收两个参数,第一个是Message对象,第二个是处理该Message的时间,事实上,Handler的一系列send()方法最终都会调用sendMessageAtTime()来发送消息。ok,再来看一下enqueueMessage()方法:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;  
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

根据方法名的意思,这一步应该是要将msg加入到MessageQueue队列当中,先在第2行的地方将msg的target设置为this,this就是当前的Hadler对象,这一步我们留到后面分析。最后调用MessageQueue的enqueueMessage()方法,这个才是真正的入队操作,enqueueMessage()就不分析了,主要的工作是将msg插入到链表中,等到Loop.loop()调用后依次取出。那么接下来就看Loop.loop()方法:

 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;

        // 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
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

            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方法还是比较长的,主要看for循环,调用的是MessageQueue的next()方法取出Message,我们看下先next()方法:

Message next() {
        //...省略
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
           //...省略
            synchronized (this) {
                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;
                }
              //...省略
            nextPollTimeoutMillis = 0;
        }
    }

主要看21行,先判断当前的时间是否小于表头的Message的处理时间,在enqueueMessage()的时候,会根据Message的处理时间来选择插入链表的位置,在表头的Message的处理时间是链表中最小的,所以比较表头的Message即可,如果小于则更新nextPollTimeoutMillis的值,如果大于则说明该Message已到时间处理,那么将其取出交给Looper。

回到Looper.loop(),可以看到,又是一个for死循环,那么这个死循环什么时候会退出呢?看到第15行,当通过MessageQueue的next()方法返回的message为空值时会退出loop循环,那next()方法什么时候返回空值?看一下next()方法的第43行,当MessageQueue的mQuitting变量为true时代表队列结束,这时候就会返回null,那mQuitting什么时候被置为true,我们看一下MessageQueue的结构图可以找到一个quit()方法。

void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

在第10行的位置将mQuitting 设置为true,那么又有疑问了,MessageQueue的quit()方法什么时候被调用?我们可以看到Looper中恰好有个quit()方法和一个quitSafely()方法,在这里就会调用MessageQueue的quit()了,这两个方法的区别就不解释了。

 public void quit() {
        mQueue.quit(false);
    }

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

由此可得,在调用Looper对象的quit()或者quitSafely()即可结束Looper.loop()的死循环。ok,再次回到Looper.loop()方法,看到第36行的 msg.target.dispatchMessage(msg),这里调用msg.target的dispatchMessage()方法。msg.target就是上面所说的Handler,到了这里,我们就知道Handler的enqueueMessage()方法中将msg.target设置成this的作用了。因为设置了target的值,所以在Looper.loop()中就可以知道处理该Message的Handler是哪一个,那么调用这个Handler的dispatchMessage()就可以把这个Message交给它处理。点进去看dispatchMessage()

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

其中,msg.callback就是一个Runnable,可能这个callback我们不太熟悉,但是我们经常会使用Handler的post()方法,先来看一下post方法

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

原来,还是调用了sendMessageDelayed()方法而已,我们点进去看getPostMessage()

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

感觉有点被忽悠了吧?其实我们调用post方法不过是将一个Message的callback设置成你传入的Runnable而已,只不过这个Message不用你手动创建,最终还是调用Handler的send()方法。搞清楚callback是什么后,再回来看dispatchMessage(),还有一个mCallback,这个mCallBack是Handler的内部接口Callback,Handler有一个构造方法可以传入CallBack

public interface Callback {
        public boolean handleMessage(Message msg);
    }

 public Handler(Callback callback) {
        this(callback, false);
    }

CallBack的作用是可以避免创建Handler的子类再重写handleMessage(),可以看到Callback的handleMessage()返回true就不会执行Handler的handleMessage(),返回false就会往下执行Handler的handleMessage(),这个方法什么都没有做,所以我们需要重写handleMessage()来处理收到的Message。

public void handleMessage(Message msg) {

 }

那么到这里为止,整个Handler的消息发送流程就基本说完了,貌似还漏了一个地方,既然Handler的创建是需要所在线程的Looper对象的,Looper对象是通过调用Looper.prepare创建的,然后再通过Looper.loop()进行轮询,但是开头的示例既没有调用Looper.prepare()也没有调用Looper.loop(),为什么不会抛出异常?其实主线程也不例外,也是需要Looper对象才能使用Handler的,所以我们可以看一下主线程的代码,主线程也就是就是main方法执行的线程,main方法在ActivityThread中,我们来看一下ActivityThread这个类:

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        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());

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

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

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

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

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

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

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

直接看21行,这里调用了Looper.prepareMainLooper(),我们看一下prepareMainLooper()

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

依旧是调用了prepare()方法,没有什么太大的区别,prepare()中传递了一个false表明该Looper不允许被退出,因为它是主线程的Looper,退出了程序就结束了…然后再将sMainLooper初始化。Loop.loop()也在main方法的37行中调用了。好了,以上就是Handler消息机制的大概流程,这里做一个总结:

1. Looper.loop()方法是一个不断轮询消息的死循环,调用Looper对象的quit()或者quitSafely()可结束循环。
2. 主线程默认会创建Looper对象以及调用Looper.loop()来轮询消息。

    我们通过源码的角度来观察会更清晰一些,由于篇幅原因还有很多地方没有说到,Handler的源码还是比较简单的,感兴趣的朋友可以深入去看,第一次写博客,很多地方阐述的不够清晰,见谅。

猜你喜欢

转载自blog.csdn.net/weixin_38261570/article/details/80816537
今日推荐