Android Handler源码浅析
相关对象
- Handler可以看作CEO,负责消息的处理和发送,Handler发送消息给MessageQueue,,然后Looper取出其中的消息给Handler。
- Looper,Handler的管家,可以看作秘书,负责管理MessageQueue,她会不断的从MessageQueue中取出消息,交给Handler处理。
- MessageQueue是存放消息的队列,负责存放Handler发来的消息。
Looper部分源码讲解
1 2 3 4 5 6 7 8 9 10
| 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)); }
|
1
| static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
|
Looper实例化是在Looper.prepare()方法中。
小插曲
ThreadLocal是一个和多线程并发相关的类。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
ThreadLocal维护了一个Map集合,其中键为当前线程,值就是不同线程的变量。
ThreadLocal中的方法不多。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); } public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
|
set方法很容易理解,首先获取当前线程,再用当前线程当键来获取map集合,如果map为空,则重新创建一个map并设置值进去,否则的话,直接设置值进去。get同理。
回到主题。所以Looper的prepare只能获取当前线程的Looper对象,并且如果已经存在一个Looper对象则会报异常,所以这也说明了一个线程中只能存在一个Lopper对象。并且prepare只能调用一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } public static void loop() { final Looper me = myLooper(); //此处只展示了关键代码 final MessageQueue queue = me.mQueue; for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } msg.recycleUnchecked(); } }
|
Looper中创建了一个MessageQueue,并且在Looper的loop方法中,开启无限循环去遍历MessageQueue,如果消息不为空,则将消息发送给Handler。这个msg.target其实就是Handler对象。
我们一般都会重写Handler的handleMessage方法,其中缘由也在源码里。
我们不管sendEmptyMessage也好,postDelayed也罢,最终都会调用到一个方法。
1 2 3 4 5 6 7
| private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
|
msg.target = this。这句话中的this就是Handler对象。
同时把消息放入MessageQueue中。
1 2 3 4 5 6 7 8 9 10 11 12
| public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
|
在Looper的loop方法中,她把消息用dispatchMessage传了过来。在dispatchMessage中。优先级有三种。优先级最高的是message自己的callback,然后是Handler的callback,当两者都没有的时候,就由我们重写来处理。这也是我们最常用的一种方法。
那么Looper是什么时候启动的呢?
答案在ActivityThread中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public static void main(String[] args) { //部分代码 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"); }
|
其中Looper.prepareMainLooper()和Looper.prepare()等价。这个ActivityThread也就是我们所说的UI线程,Looper在UI线程启动的时候,就一直在后台默默工作了。
思考,Handler怎么做到线程切换处理对象的。
因为不同线程共享内存,Handler在A线程发送了一个消息,然后主线程的Looper在主线程把消息取了出来,同时交给了Handler,所以Handler一取一存就做到了线程切换。
小结
其实看源码本没有那么复杂,有时候根本不需要抽丝剥茧一条条代码看,代码量很多,难度确实是有的。但是我们只要有一个目标,有一条主线,只寻找最关键的代码,就能很快的理清它的脉络。