查看原文
其他

面试必备:ThreadLocal+Looper+Handler

胡飞洋 胡飞洋 2022-07-18

文章目录

  • 一、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使用步骤

  1. 在任务执行的线程,使用Looper.prepare()来给线程创建Looper实例。

  2. 在任务执行的线程,创建Handler实例。

  3. 在任务执行的线程,使用Looper.loop()开启消息循环。

  4. 任务发出的线程,使用Handler实例发送消息。


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


1    @Override
2    protected void onCreate(Bundle savedInstanceState) {
3        super.onCreate(savedInstanceState);
4        setContentView(R.layout.activity_main);
5
6        testHandler();
7    }
8
9    private void testHandler() {
10
11        new Thread(new Runnable() {
12            @Override
13            public void run() {
14                //1、准备looper,即threadLocal<Looper>.set(new Looper())
15                Looper.prepare();
16

17                //2、创建handler实例
18                // 这个重写了handleMessage,handler是属于Handler的子类的实例

19                mHandler = new Handler() {
20                    @Override

21                    public void handleMessage(Message msg) {
22                        super.handleMessage(msg);
23                        Log.i(TAG, "child thread, handleMessage: what="+msg.what);
24                    }
25                };
26
27                //3、looper启动,sThreadLocal.get()拿到looper,拿到queue,开始queue.next
28                Looper.loop();

29            }
30        }).start();
31    }
32
33    public void onClick(){
34        //4.2、handler.sendMessage发送消息,queue.enqueueMessage(msg),即消息入队列。
35        Log.i(TAG, "main thread, sendMessage");
36        Message message = Message.obtain();
37        message.what = 100;
38        mHandler.sendMessage(message);   
39    }


1.2Handler的使用背景


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

1    void checkThread() {
2        if (mThread != Thread.currentThread()) {
3            throw new CalledFromWrongThreadException(

4                    "Only the original thread that created a view hierarchy can touch its views.");
5        }

6    }


而且,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(),所在线程不同,结果就不同。

1         ThreadLocal<Boolean> booleanThreadLocal = new ThreadLocal<>();
2        ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();
3
4        booleanThreadLocal.set(true);
5        integerThreadLocal.set(0);
6        Log.i(TAG, "testThreadLocal: main thread, boolean= "+booleanThreadLocal.get());
7        Log.i(TAG, "testThreadLocal: main thread, int = "+integerThreadLocal.get());
8
9        new Thread(new Runnable() {
10            @Override
11            public void run() {
12                booleanThreadLocal.set(false);
13                integerThreadLocal.set(1);
14                Log.i(TAG, "testThreadLocal: a thread, boolean="+booleanThreadLocal.get());
15                Log.i(TAG, "testThreadLocal: a thread, int = "+integerThreadLocal.get());
16            }
17        }).start();
18
19        new Thread(new Runnable() {
20            @Override
21            public void run() {
22                booleanThreadLocal.set(null);
23                integerThreadLocal.set(2);
24                Log.i(TAG, "testThreadLocal: b thread, boolean="+booleanThreadLocal.get());
25                Log.i(TAG, "testThreadLocal: b thread, int = "+integerThreadLocal.get());
26            }
27        }).start();


结果:

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


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

1    /**
2     * Returns the value in the current thread's copy of this
3     * thread-local variable.  If the variable has no value for the
4     * current thread, it is first initialized to the value returned
5     * by an invocation of the {@link #initialValue} method.
6     *
7     * @return the current thread's value of this thread-local
8     */
9    public T get() {
10        Thread t = Thread.currentThread();
11        ThreadLocalMap map = getMap(t);
12        if (map != null) {
13            ThreadLocalMap.Entry e = map.getEntry(this);
14            if (e != null) {
15                @SuppressWarnings("unchecked")
16                T result = (T)e.value;
17                return result;
18            }
19        }
20        return setInitialValue();
21    }
22
23    /**
24     * Variant of set() to establish initialValue. Used instead
25     * of set() in case user has overridden the set() method.
26     *
27     * @return the initial value
28     */
29    private T setInitialValue() {
30        T value = initialValue();
31        Thread t = Thread.currentThread();
32        ThreadLocalMap map = getMap(t);
33        if (map != null)
34            map.set(this, value);
35        else
36            createMap(t, value);
37        return value;
38    }


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

1    /**
2     * Sets the current thread's copy of this thread-local variable
3     * to the specified value.  Most subclasses will have no need to
4     * override this method, relying solely on the {@link #initialValue}
5     * method to set the values of thread-locals.
6     *
7     * @param value the value to be stored in the current thread's copy of
8     *        this thread-local.
9     */
10    public void set(T value) {
11        Thread t = Thread.currentThread();
12        ThreadLocalMap map = getMap(t);
13        if (map != null)
14            map.set(this, value);
15        else
16            createMap(t, value);
17    }
18     /**
19     * Create the map associated with a ThreadLocal. Overridden in
20     * InheritableThreadLocal.
21     *
22     * @param t the current thread
23     * @param firstValue value for the initial entry of the map
24     */
25    void createMap(Thread t, T firstValue) {
26        t.threadLocals = new ThreadLocalMap(this, firstValue);
27    }

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


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

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

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


1  /**
2   * Construct a new map initially containing (firstKey, firstValue).
3   * ThreadLocalMaps are constructed lazily, so we only create
4   * one when we have at least one entry to put in it.
5   */
6  ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
7      table = new Entry[INITIAL_CAPACITY];
8      int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
9      table[i] = new Entry(firstKey, firstValue);
10      size = 1;
11      setThreshold(INITIAL_CAPACITY);
12  }
13
14  /**
15   * Get the entry associated with key.  This method
16   * itself handles only the fast path: a direct hit of existing
17   * key. It otherwise relays to getEntryAfterMiss.  This is
18   * designed to maximize performance for direct hits, in part
19   * by making this method readily inlinable.
20   *
21   * @param  key the thread local object
22   * @return the entry associated with key, or null if no such
23   */
24  private Entry getEntry(ThreadLocal<?> key) {
25      int i = key.threadLocalHashCode & (table.length - 1);
26      Entry e = table[i];
27      if (e != null && e.get() == key)
28          return e;
29      else
30          return getEntryAfterMiss(key, i, e);
31  }
32
33  /**
34   * Set the value associated with key.
35   *
36   * @param key the thread local object
37   * @param value the value to be set
38   */
39  private void set(ThreadLocal<?> key, Object value) {
40
41      // We don't use a fast path as with get() because it is at
42      // least as common to use set() to create new entries as
43      // it is to replace existing ones, in which case, a fast
44      // path would fail more often than not.
45
46      Entry[] tab = table;
47      int len = tab.length;
48      int i = key.threadLocalHashCode & (len-1);
49
50      for (Entry e = tab[i];
51           e != null;
52           e = tab[i = nextIndex(i, len)]) {
53          ThreadLocal<?> k = e.get();
54
55          if (k == key) {
56              e.value = value;
57              return;
58          }
59
60          if (k == null) {
61              replaceStaleEntry(key, value, i);
62              return;
63          }
64      }
65
66      tab[i] = new Entry(key, value);
67      int sz = ++size;
68      if (!cleanSomeSlots(i, sz) && sz >= threshold)
69          rehash();
70  }


使用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)

1         ThreadLocal<Boolean> booleanThreadLocal = new ThreadLocal<>();
2        ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();
3
4        booleanThreadLocal.set(true);
5        integerThreadLocal.set(0);
6        Log.i(TAG, "testThreadLocal: main thread, boolean= "+booleanThreadLocal.get());
7        Log.i(TAG, "testThreadLocal: main thread, int = "+integerThreadLocal.get());
8
9        new Thread(new Runnable() {
10            @Override
11            public void run() {
12                booleanThreadLocal.set(false);
13                integerThreadLocal.set(1);
14                Log.i(TAG, "testThreadLocal: a thread, boolean="+booleanThreadLocal.get());
15                Log.i(TAG, "testThreadLocal: a thread, int = "+integerThreadLocal.get());
16            }
17        }).start();
18
19        new Thread(new Runnable() {
20            @Override
21            public void run() {
22                booleanThreadLocal.set(null);
23                integerThreadLocal.set(2);
24                Log.i(TAG, "testThreadLocal: b thread, boolean="+booleanThreadLocal.get());
25                Log.i(TAG, "testThreadLocal: b thread, int = "+integerThreadLocal.get());
26            }
27        }).start();


结果:

12020-01-08 10:15:38.623 8976-8976/com.hfy.demo01 I/hfy: testThreadLocal: main thread, boolean= true
22020-01-08 10:15:38.623 8976-8976/com.hfy.demo01 I/hfy: testThreadLocal: main thread, int = 0
32020-01-08 10:15:38.624 8976-9226/com.hfy.demo01 I/hfy: testThreadLocal: a thread, boolean=false
42020-01-08 10:15:38.624 8976-9226/com.hfy.demo01 I/hfy: testThreadLocal: a thread, int = 1
52020-01-08 10:15:38.626 8976-9227/com.hfy.demo01 I/hfy: testThreadLocal: b thread, boolean=null
62020-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(),存消息,单链表的插入。


1    boolean enqueueMessage(Message msg, long when) {
2        if (msg.target == null) {
3            throw new IllegalArgumentException("Message must have a target.");
4        }
5        if (msg.isInUse()) {
6            throw new IllegalStateException(msg + " This message is already in use.");
7        }
8
9        synchronized (this) {
10            if (mQuitting) {
11                IllegalStateException e = new IllegalStateException(
12                        msg.target + " sending message to a Handler on a dead thread");
13                Log.w(TAG, e.getMessage(), e);
14                msg.recycle();
15                return false;
16            }
17
18            msg.markInUse();
19            msg.when = when;
20            Message p = mMessages;
21            boolean needWake;
22            if (p == null || when == 0 || when < p.when) {
23                // New head, wake up the event queue if blocked.
24                msg.next = p;
25                mMessages = msg;
26                needWake = mBlocked;
27            } else {
28                // Inserted within the middle of the queue.  Usually we don't have to wake
29                // up the event queue unless there is a barrier at the head of the queue
30                // and the message is the earliest asynchronous message in the queue.
31                needWake = mBlocked && p.target == null && msg.isAsynchronous();
32                Message prev;
33                for (;;) {
34                    prev = p;
35                    p = p.next;
36                    if (p == null || when < p.when) {
37                        break;
38                    }
39                    if (needWake && p.isAsynchronous()) {
40                        needWake = false;
41                    }
42                }
43                msg.next = p; // invariant: p == prev.next
44                prev.next = msg;
45            }
46
47            // We can assume mPtr != 0 because mQuitting is false.
48            if (needWake) {
49                nativeWake(mPtr);
50            }
51        }
52        return true;
53    }


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

1    Message next() {
2        //...
3        //有msg就return,没有消息就无限循环,会阻塞。如quit,return null。
4        for (;;) {
5            if (nextPollTimeoutMillis != 0) {
6                Binder.flushPendingCommands();
7            }
8
9            nativePollOnce(ptr, nextPollTimeoutMillis);
10
11            synchronized (this) {
12                // Try to retrieve the next message.  Return if found.
13                final long now = SystemClock.uptimeMillis();
14                Message prevMsg = null;
15                Message msg = mMessages;
16                if (msg != null && msg.target == null) {
17                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
18                    do {
19                        prevMsg = msg;
20                        msg = msg.next;
21                    } while (msg != null && !msg.isAsynchronous());
22                }
23                if (msg != null) {
24                    if (now < msg.when) {
25                        // Next message is not ready.  Set a timeout to wake up when it is ready.
26                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
27                    } else {
28                        // Got a message.
29                        mBlocked = false;
30                        if (prevMsg != null) {
31                            prevMsg.next = msg.next;
32                        } else {
33                            mMessages = msg.next;
34                        }
35                        msg.next = null;
36                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
37                        msg.markInUse();
38                        //有消息就return
39                        return msg;
40                    }
41                } else {
42                    // No more messages.
43                    nextPollTimeoutMillis = -1;
44                }
45
46                // Process the quit message now that all pending messages have been handled.
47                if (mQuitting) {
48                    dispose();
49                    //quit后返回null
50                    return null;
51                }
52
53                // ...
54    }


2.3 Looper

looper,消息循环器。

先看静态方法prepare():

1    // sThreadLocal.get() will return null unless you've called prepare().
2    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
3
4    /** Initialize the current thread as a looper.
5      * This gives you a chance to create handlers that then reference
6      * this looper, before actually starting the loop. Be sure to call
7      * {@link #loop()} after calling this method, and end it by calling
8      * {@link #quit()}.
9      */
10    public static void prepare() {
11        prepare(true);
12    }
13
14    private static void prepare(boolean quitAllowed) {
15        if (sThreadLocal.get() != null) {
16            throw new RuntimeException("Only one Looper may be created per thread");
17        }
18        sThreadLocal.set(new Looper(quitAllowed));
19    }
20
21    /**
22     * Initialize the current thread as a looper, marking it as an
23     * application's main looper. The main looper for your application
24     * is created by the Android environment, so you should never need
25     * to call this function yourself.  See also: {@link #prepare()}
26     */
27    public static void prepareMainLooper() {
28        prepare(false);
29        synchronized (Looper.class) {
30            if (sMainLooper != null) {
31                throw new IllegalStateException("The main Looper has already been prepared.");
32            }
33            sMainLooper = myLooper();
34        }
35    }
36
37    private Looper(boolean quitAllowed) {
38        mQueue = new MessageQueue(quitAllowed);
39        mThread = Thread.currentThread();
40    }


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

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


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

1    /**
2     * Returns the application's main looper, which lives in the main thread of the application.
3     */
4    public static Looper getMainLooper() {
5        synchronized (Looper.class) {
6            return sMainLooper;
7        }
8    }
9
10    /**
11     * Return the Looper object associated with the current thread.  Returns
12     * null if the calling thread is not associated with a Looper.
13     */
14    public static @Nullable Looper myLooper() {
15        return sThreadLocal.get();
16    }
17
18    /**
19     * Return the {@link MessageQueue} object associated with the current
20     * thread.  This must be called from a thread running a Looper, or a
21     * NullPointerException will be thrown.
22     */
23    public static @NonNull MessageQueue myQueue() {
24        return myLooper().mQueue;
25    }


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

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

1    /**
2     * Quits the looper.
3     * <p>
4     * Causes the {@link #loop} method to terminate without processing any
5     * more messages in the message queue.
6     * </p><p>
7     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
8     * For example, the {@link Handler#sendMessage(Message)} method will return false.
9     * </p><p class="note">
10     * Using this method may be unsafe because some messages may not be delivered
11     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
12     * that all pending work is completed in an orderly manner.
13     * </p>
14     *
15     * @see #quitSafely
16     */
17    public void quit() {
18        mQueue.quit(false);
19    }
20
21    /**
22     * Quits the looper safely.
23     * <p>
24     * Causes the {@link #loop} method to terminate as soon as all remaining messages
25     * in the message queue that are already due to be delivered have been handled.
26     * However pending delayed messages with due times in the future will not be
27     * delivered before the loop terminates.
28     * </p><p>
29     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
30     * For example, the {@link Handler#sendMessage(Message)} method will return false.
31     * </p>
32     */
33    public void quitSafely() {
34        mQueue.quit(true);
35    }


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


1    /**
2     * Run the message queue in this thread. Be sure to call
3     * {@link #quit()} to end the loop.
4     */
5    public static void loop() {
6        final Looper me = myLooper();
7        if (me == null) {
8            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
9        }
10        final MessageQueue queue = me.mQueue;
11
12        // ...
13
14        for (;;) {
15            //没有msg ,queue.next()阻塞,loop() 也就阻塞了。next有msg就处理,无限循环。
16            Message msg = queue.next(); // might block
17            if (msg == null) {
18                //调用quit()时才会 跳出循环
19                // No message indicates that the message queue is quitting.
20                return;
21            }
22
23            // ...
24            //用target(handler)处理消息,dispatchMessage执行在loop() 调用的地方,即looper所在线程。
25            try {
26                msg.target.dispatchMessage(msg);
27                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
28            } finally {
29                if (traceTag != 0) {
30                    Trace.traceEnd(traceTag);
31                }
32            }
33            ...
34        }
35    }

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


2.4 Handler

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

1    /**
2     * Default constructor associates this handler with the {@link Looper} for the
3     * current thread.
4     *
5     * If this thread does not have a looper, this handler won't be able to receive messages
6     * so an exception is thrown.
7     */
8    public Handler() {
9        this(null, false);
10    }
11
12    public Handler(Callback callback) {
13        this(callback, false);
14    }
15
16    public Handler(Callback callback, boolean async) {
17        ...
18        mLooper = Looper.myLooper();
19        if (mLooper == null) {
20            throw new RuntimeException(
21                "Can't create handler inside thread " + Thread.currentThread()
22                        + " that has not called Looper.prepare()");
23        }
24        mQueue = mLooper.mQueue;
25        mCallback = callback;
26        mAsynchronous = async;
27    }


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

1    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
2        msg.target = this;
3        if (mAsynchronous) {
4            msg.setAsynchronous(true);
5        }
6        return queue.enqueueMessage(msg, uptimeMillis);
7    }


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

1    /**
2     * Handle system messages here.
3     */
4    public void dispatchMessage(Message msg) {
5        if (msg.callback != null) {
6            //msg.callback就是handler.post()发送的runable
7            handleCallback(msg);
8        } else {
9            if (mCallback != null) {
10                //mCallback是创建Handler时传入CallBack的情况。
11                if (mCallback.handleMessage(msg)) {
12                    return;
13                }
14            }
15            //覆写handleMessage()创建handler的情况
16            handleMessage(msg);
17        }
18    }


三、主线程的消息机制


主线程的消息

Looper中:

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


ActivityThread的静态方法main:

1    final H mH = new H();
2
3    public static void main(String[] args) {
4        ...
5        //1、准备主线程的Looper
6        Looper.prepareMainLooper();
7
8        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
9        // It will be in the format "seq=114"
10        long startSeq = 0;
11        if (args != null) {
12            for (int i = args.length - 1; i >= 0; --i) {
13                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
14                    startSeq = Long.parseLong(
15                            args[i].substring(PROC_START_SEQ_IDENT.length()));
16                }
17            }
18        }
19        //这里实例化ActivityThread,也就实例化了上面的mH,就是handler。
20        ActivityThread thread = new ActivityThread();
21        thread.attach(false, startSeq);
22
23        //获取handler
24        if (sMainThreadHandler == null) {
25            sMainThreadHandler = thread.getHandler();
26        }
27
28        ...
29        //主线程looper开启
30        Looper.loop();
31        //因为主线程的Looper是不能退出的,退出就无法接受事件了。一旦意外退出,会抛出异常
32        throw new RuntimeException("Main thread loop unexpectedly exited");
33    }


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


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存