ThreadLocal+Looper+Handler

137 阅读9分钟

目录

一、Handler使用与概述

  • 1.1使用步骤
  • 1.2Handler的使用背景

二、Android消息机制分析

  • 2.1 ThreadLocal
  • 2.2 messageQueue
  • 2.3 Looper
  • 2.4 Handler
  • 三、主线程的消息机制

Handler是消息机制的上层接口,开发中基本只用和Handler交互即可。Handler可以将一个任务切换到Handler指定的线程中执行。如在用Handler在子线程更新UI。 Android消息机制主要就是Handler的运行机制。Handler的运行还依赖MessageQueue、Looper,及Looper内部使用到的ThreadLocal。 MessageQueue是消息队列,用于存放Handler发送的消息,实际是单链表的结构。 Looper会在消息队列中无限循环的查找消息,有消息就取出,没有就等待。 ThreadLocal本质作用是在每个线程中存储数据。在Looper中的作用就是给每个线程存Looper实例。因为我们知道,创建Handler时是需要线程的Looper实例的,而非UI线程默认是没有Looper的。

一、Handler使用与概述 1.1使用步骤 在任务执行的线程,使用Looper.prepare()来给线程创建Looper实例。 在任务执行的线程,创建Handler实例。 在任务执行的线程,使用Looper.loop()开启消息循环。 在任务发出的线程,使用Handler实例发送消息。 举个例子? 如下所示,点击按钮,在主线程发送消息,就会在子线程执行。 (这个例子为了完整展示使用步骤,所以在子线程创建了handler,在主线程发送和消息。通常实际我们使用是在主线程创建handler,在子线程发送消息然后再主线程执行UI的更新,而主线程默认是有Looper并开启的,所以一般不需要第一步和第三部。)

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    testHandler();
}

private void testHandler() {

    new Thread(new Runnable() {
        @Override
        public void run() {
            //1、准备looper,即threadLocal<Looper>.set(new Looper())
            Looper.prepare();

            //2、创建handler实例
            // 这个重写了handleMessage,handler是属于Handler的子类的实例
            mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    Log.i(TAG, "child thread, handleMessage: what="+msg.what);
                }
            };

            //3、looper启动,sThreadLocal.get()拿到looper,拿到queue,开始queue.next
            Looper.loop();
        }
    }).start();
}

public void onClick(){
    //4.2、handler.sendMessage发送消息,queue.enqueueMessage(msg),即消息入队列。
    Log.i(TAG, "main thread, sendMessage");
    Message message = Message.obtain();
    message.what = 100;
    mHandler.sendMessage(message);   
}

1.2Handler的使用背景 Handler可以将子线程中更新UI的任务切换到主线程。为什么要切换呢?我们知道,UI的访问只能在主线程进行。子线程访问UI就会出现异常,因为在ViewRootImpl中对线程做了校验,只有创建了这个View树的线程,才能访问这个view。 一般情况创建View的线程就是主线程,即UI线程,所以子线程访问会异常。

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

而且,UI线程一般不能做耗时操作,不然会发生ANR。所以当 在子线程做完耗时操作后 又需要更新UI,这时就需要用到Handler了。那为啥一定要用checkThread()保证不让子线程访问UI呢?因为UI控件不是线程安全的。那为啥不加锁呢?一是加锁会让UI访问变得复杂;二是加锁会降低UI访问效率,会阻塞一些线程访问UI。所以干脆使用单线程模型处理UI操作,使用时用Handler切换即可。

二、Android消息机制分析 前面说了,Android消息机制包含几个概念:Handler、MessageQueue、Looper、Looper内部使用到的ThreadLocal。下面详细介绍下。

2.1 ThreadLocal 外界想要在不同thread中存值,就可以threadLocal = new ThreadLocal,然后在不同线程中threadLocal.set(value)就可以了,获取值用threadLocal.get() 。

举个例子?,下面例子中 先只看booleanThreadLocal,在主线程设置true,a线程设置false,b线程设置null,然后每个线程都打印 booleanThreadLocal.get()的结果,发现每个线程get的值是不同的,是在每个线程中set的值。这就是神奇之处,同样的booleanThreadLocal.get(),所在线程不同,结果就不同。

     ThreadLocal<Boolean> booleanThreadLocal = new ThreadLocal<>();
    ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();

    booleanThreadLocal.set(true);
    integerThreadLocal.set(0);
    Log.i(TAG, "testThreadLocal: main thread, boolean= "+booleanThreadLocal.get());
    Log.i(TAG, "testThreadLocal: main thread, int = "+integerThreadLocal.get());

    new Thread(new Runnable() {
        @Override
        public void run() {
            booleanThreadLocal.set(false);
            integerThreadLocal.set(1);
            Log.i(TAG, "testThreadLocal: a thread, boolean="+booleanThreadLocal.get());
            Log.i(TAG, "testThreadLocal: a thread, int = "+integerThreadLocal.get());
        }
    }).start();

    new Thread(new Runnable() {
        @Override
        public void run() {
            booleanThreadLocal.set(null);
            integerThreadLocal.set(2);
            Log.i(TAG, "testThreadLocal: b thread, boolean="+booleanThreadLocal.get());
            Log.i(TAG, "testThreadLocal: b thread, int = "+integerThreadLocal.get());
        }
    }).start();

结果:

2020-01-08 10:15:38.623 8976-8976/com.hfy.demo01 I/hfy: testThreadLocal: main thread, boolean= true
2020-01-08 10:15:38.623 8976-8976/com.hfy.demo01 I/hfy: testThreadLocal: main thread, int = 0
2020-01-08 10:15:38.624 8976-9226/com.hfy.demo01 I/hfy: testThreadLocal: a thread, boolean=false
2020-01-08 10:15:38.624 8976-9226/com.hfy.demo01 I/hfy: testThreadLocal: a thread, int = 1
2020-01-08 10:15:38.626 8976-9227/com.hfy.demo01 I/hfy: testThreadLocal: b thread, boolean=null
2020-01-08 10:15:38.626 8976-9227/com.hfy.demo01 I/hfy: testThreadLocal: b thread, int = 2

下面看下ThreadLocal的get()、set()方法。

/**
 * Returns the value in the current thread's copy of this
 * thread-local variable.  If the variable has no value for the
 * current thread, it is first initialized to the value returned
 * by an invocation of the {@link #initialValue} method.
 *
 * @return the current thread's value of this thread-local
 */
public T get() {
    Thread t = Thread.currentThread();
    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;
        }
    }
    return setInitialValue();
}

/**
 * Variant of set() to establish initialValue. Used instead
 * of set() in case user has overridden the set() method.
 *
 * @return the initial value
 */
private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

get():获取当前线程的ThreadLocalMap,这里可以先理解成普通 键值对的Map。然后传入threadLocal实例,获取键值对Entry,然后获取Entry的value。如果map为空或value为空则会初始化map、value。

/**
 * Sets the current thread's copy of this thread-local variable
 * to the specified value.  Most subclasses will have no need to
 * override this method, relying solely on the {@link #initialValue}
 * method to set the values of thread-locals.
 *
 * @param value the value to be stored in the current thread's copy of
 *        this thread-local.
 */
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
 /**
 * Create the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @param t the current thread
 * @param firstValue value for the initial entry of the map
 */
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

set()中也是获取当前线程的ThreadLocalMap,然后ThreadLocal实例作为key, 和value一起设置给map。没有map就去创建并把value初始化进去。

我们再去看下Thread,有个默认为空的ThreadLocalMap实例threadLocals。

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

那ThreadLocalMap是啥呢?ThreadLocalMap是ThreadLocal的内部类,作用类似Map,内部有个Entry[]的属性table。所以上面看的get、set方法就是对ThreadLocalMap的Entry[]取和存 。下面详细看下。

  /**
   * Construct a new map initially containing (firstKey, firstValue).
   * ThreadLocalMaps are constructed lazily, so we only create
   * one when we have at least one entry to put in it.
   */
  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);
  }

  /**
   * Get the entry associated with key.  This method
   * itself handles only the fast path: a direct hit of existing
   * key. It otherwise relays to getEntryAfterMiss.  This is
   * designed to maximize performance for direct hits, in part
   * by making this method readily inlinable.
   *
   * @param  key the thread local object
   * @return the entry associated with key, or null if no such
   */
  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);
  }

  /**
   * Set the value associated with key.
   *
   * @param key the thread local object
   * @param value the value to be 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.

      Entry[] tab = table;
      int len = tab.length;
      int i = key.threadLocalHashCode & (len-1);

      for (Entry e = tab[i];
           e != null;
           e = tab[i = nextIndex(i, len)]) {
          ThreadLocal<?> k = e.get();

          if (k == key) {
              e.value = value;
              return;
          }

          if (k == null) {
              replaceStaleEntry(key, value, i);
              return;
          }
      }

      tab[i] = new Entry(key, value);
      int sz = ++size;
      if (!cleanSomeSlots(i, sz) && sz >= threshold)
          rehash();
  }

使用Entry[] 存多个threadLocal-value键值对,数组下标index与是ThreadLocal 实例的hashCode相关。而ThreadLocalMap唯一实例是createMap(Thread t, T firstValue)赋给Thread的变量threadLocals。
例如 线程A threadLocalMap的table[] 可以存储 int、String、boolean类型的3个键值对threadLocal-int, threadLocal-String、threadLocal-Boolean。还是上面的例子。

(常规的HashMap的键值得类型是固定的;threadLocalMap的key是ThreadLocal,value是T,即可以存多种类型的value)

     ThreadLocal<Boolean> booleanThreadLocal = new ThreadLocal<>();
    ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();

    booleanThreadLocal.set(true);
    integerThreadLocal.set(0);
    Log.i(TAG, "testThreadLocal: main thread, boolean= "+booleanThreadLocal.get());
    Log.i(TAG, "testThreadLocal: main thread, int = "+integerThreadLocal.get());

    new Thread(new Runnable() {
        @Override
        public void run() {
            booleanThreadLocal.set(false);
            integerThreadLocal.set(1);
            Log.i(TAG, "testThreadLocal: a thread, boolean="+booleanThreadLocal.get());
            Log.i(TAG, "testThreadLocal: a thread, int = "+integerThreadLocal.get());
        }
    }).start();

    new Thread(new Runnable() {
        @Override
        public void run() {
            booleanThreadLocal.set(null);
            integerThreadLocal.set(2);
            Log.i(TAG, "testThreadLocal: b thread, boolean="+booleanThreadLocal.get());
            Log.i(TAG, "testThreadLocal: b thread, int = "+integerThreadLocal.get());
        }
    }).start();

结果:

2020-01-08 10:15:38.623 8976-8976/com.hfy.demo01 I/hfy: testThreadLocal: main thread, boolean= true
2020-01-08 10:15:38.623 8976-8976/com.hfy.demo01 I/hfy: testThreadLocal: main thread, int = 0
2020-01-08 10:15:38.624 8976-9226/com.hfy.demo01 I/hfy: testThreadLocal: a thread, boolean=false
2020-01-08 10:15:38.624 8976-9226/com.hfy.demo01 I/hfy: testThreadLocal: a thread, int = 1
2020-01-08 10:15:38.626 8976-9227/com.hfy.demo01 I/hfy: testThreadLocal: b thread, boolean=null
2020-01-08 10:15:38.626 8976-9227/com.hfy.demo01 I/hfy: testThreadLocal: b thread, int = 2

到目前为止我们知道,ThreadLocal的作用,就是操作线程内部的threadLocals,存和取value。value的实际类型就是 实例化ThreadLocal时定义的泛型T。

2.2 messageQueue messageQueue,消息队列,实际是单向链表。看下存、取消息。

enqueueMessage(),存消息,单链表的插入。

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

next():取一条消息,没有消息就无限循环,会阻塞。

Message next() {
    //...
    //有msg就return,没有消息就无限循环,会阻塞。如quit,return null。
    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
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                //quit后返回null
                return null;
            }

            // ...
}

2.3 Looper looper,消息循环器。

先看静态方法prepare():

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

/** Initialize the current thread as a looper.
  * This gives you a chance to create handlers that then reference
  * this looper, before actually starting the loop. Be sure to call
  * {@link #loop()} after calling this method, and end it by calling
  * {@link #quit()}.
  */
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));
}

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
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 Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

可见sThreadLocal是个静态常量,value类型是Looper。 prepare()方法调sThreadLocal.set(new Looper),创建looper实例,设置给当前线程ThreadLocalMap属性中的table[i](i是threadLocal实例的hashCode相关)。

且创建looper实例时默认创建了对应的消息队列mQueue实例。另外,prepareMainLooper()是主线程,是给主线程创建looper实例。

再看下获取looper实例、queue实例的方法:

/**
 * Returns the application's main looper, which lives in the main thread of the application.
 */
public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}

/**
 * Return the Looper object associated with the current thread.  Returns
 * null if the calling thread is not associated with a Looper.
 */
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

/**
 * Return the {@link MessageQueue} object associated with the current
 * thread.  This must be called from a thread running a Looper, or a
 * NullPointerException will be thrown.
 */
public static @NonNull MessageQueue myQueue() {
    return myLooper().mQueue;
}

myLooper() 方法,调用sThreadLocal.get()。就是上面讲解的ThreadLocal的使用方法。通过静态常量sThreadLocal获取对应每个线程的Looper实例。

looper的quit,两种,立即退出,执行完消息再退出。

/**
 * Quits the looper.
 * <p>
 * Causes the {@link #loop} method to terminate without processing any
 * more messages in the message queue.
 * </p><p>
 * Any attempt to post messages to the queue after the looper is asked to quit will fail.
 * For example, the {@link Handler#sendMessage(Message)} method will return false.
 * </p><p class="note">
 * Using this method may be unsafe because some messages may not be delivered
 * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
 * that all pending work is completed in an orderly manner.
 * </p>
 *
 * @see #quitSafely
 */
public void quit() {
    mQueue.quit(false);
}

/**
 * Quits the looper safely.
 * <p>
 * Causes the {@link #loop} method to terminate as soon as all remaining messages
 * in the message queue that are already due to be delivered have been handled.
 * However pending delayed messages with due times in the future will not be
 * delivered before the loop terminates.
 * </p><p>
 * Any attempt to post messages to the queue after the looper is asked to quit will fail.
 * For example, the {@link Handler#sendMessage(Message)} method will return false.
 * </p>
 */
public void quitSafely() {
    mQueue.quit(true);
}

静态方法loop():用threadLocal.get()获取当前线程的Looper,然后拿到queue,循环取消息,给到handler的dispatchMessage方法-handleMessage方法。唯一跳出循环是取到null,null是因为调用了quit或quitSafly。 因为静态方法loop()是在线程中调用的,所以不论handler从哪里发送msg都会在loop的线程中执行。

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the 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;

    // ...

    for (;;) {
        //没有msg ,queue.next()阻塞,loop() 也就阻塞了。next有msg就处理,无限循环。
        Message msg = queue.next(); // might block
        if (msg == null) {
            //调用quit()时才会 跳出循环
            // No message indicates that the message queue is quitting.
            return;
        }

        // ...
        //用target(handler)处理消息,dispatchMessage执行在loop() 调用的地方,即looper所在线程。
        try {
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        ...
    }
}

流程 prepare()-new hanler()- loop() 连续的在同个线程调用。保证handleMessage执行在当前线程。即使handler.sengMessage()在其他线程调用。

2.4 Handler 发送,处理消息。 先看Handler构造方法,可见调用了Looper.myLooper(),就是获取当前线程的looper,没有就会抛出异常。

/**
 * Default constructor associates this handler with the {@link Looper} for the
 * current thread.
 *
 * If this thread does not have a looper, this handler won't be able to receive messages
 * so an exception is thrown.
 */
public Handler() {
    this(null, false);
}

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

public Handler(Callback callback, boolean async) {
    ...
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

发送消息,就是把消息放入队列

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

处理消息,根据Handler的创建形式和使用方法对应处理。

/**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        //msg.callback就是handler.post()发送的runable
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            //mCallback是创建Handler时传入CallBack的情况。
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //覆写handleMessage()创建handler的情况
        handleMessage(msg);
    }
}

三、主线程的消息机制 主线程的消息

Looper中:

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

ActivityThread的静态方法main:

final H mH = new H();

public static void main(String[] args) {
    ...
    //1、准备主线程的Looper
    Looper.prepareMainLooper();

    // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
    // It will be in the format "seq=114"
    long startSeq = 0;
    if (args != null) {
        for (int i = args.length - 1; i >= 0; --i) {
            if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                startSeq = Long.parseLong(
                        args[i].substring(PROC_START_SEQ_IDENT.length()));
            }
        }
    }
    //这里实例化ActivityThread,也就实例化了上面的mH,就是handler。
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    //获取handler
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    ...
    //主线程looper开启
    Looper.loop();
    //因为主线程的Looper是不能退出的,退出就无法接受事件了。一旦意外退出,会抛出异常
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

H处理了四大组件的启动停止等。ActivityThread通过ApplicationThread和AMS进行进程间通信,AMS完成ActivityThread的请求后,回调到ApplicationThread中的binder方法,然后ApplicationThread使用H发送消息,然后就把此消息切换到ApplicationThread中执行,即在主线程执行。 这就是主线程的消息循环。