Handler机制
申明:低级码农问题解决中的参考和解决后的小结,仅用于个人记录,能力有限,可能有些错误,缺陷不自知,欢迎在评论中指正,谢谢!
Handler机制是什么,一般我们都会回答,一个Handler必须关联一个线程,通过Looper来实现的,一个Looper对应一个MessageQueue。其他线程通过Handler把消息放到MessageQueue中,Looper遍历MessageQueue,然后把消息交给Hanlder处理。但实际使用过程中,我们使用Handler最常用的场景是在子线程中过去数据后,使用Handler来通知UI线程,即主线程更新UI。在这个过程中,似乎是没有涉及到Looper,MessageQueue,线程和handler的关联过程等内容。其实这是因为应用的启动过程中,系统已经替我们把上面的工作完成了。如果要了解Handler的工作过程,还是看子线程的Handler比较好。
下面以HandlerThread为例,讲一下Handler与子线程的关联过程,下面是HandlerThread的run方法。
1 public void run() { 2 Looper.prepare(); 3 ... 4 Looper.loop(); 5 }
Looper.prepare中完成了Looper,MessageQueue的创建。MessageQueue是Looper的成员变量,所以一个Looper对应一个MessageQueue。
1 private static void prepare(boolean quitAllowed) { 2 ...... 3 sThreadLocal.set(new Looper(quitAllowed)); 4 } 5 6 private Looper(boolean quitAllowed) { 7 mQueue = new MessageQueue(quitAllowed); 8 mThread = Thread.currentThread(); 9 }
Looper和线程的关联过程如下,见ThreadLocal的set方法。获取当前线程,然后把Looper与之关联。
1 public void set(T value) { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map = getMap(t); 4 if (map != null) 5 map.set(this, value); 6 else 7 createMap(t, value); 8 } 9 10 void createMap(Thread t, T firstValue) { 11 t.threadLocals = new ThreadLocalMap(this, firstValue); 12 }
Looper.loop无限循环遍历MessageQueue,并分发。值得注意的是,这个for循环是死循环,为什么不会导致程序anr?我不清楚原理(Linux的epoll模型),但结论是:在queue.next()时,线程阻塞,释放CPU资源。
1 public static void loop() { 2 ... 3 for (;;) { 4 Message msg = queue.next(); // might block 5 if (msg == null) { 6 return; 7 } 8 ... 9 msg.target.dispatchMessage(msg); 10 } 11 }
向MessageQueue中压入消息和获取消息,是异步的,这里就不贴代码了,详细可见MessageQueue的enqueueMessage方法和next方法。
前面提到了线程和Looper关联,那Handler如何与Looper关联呢。如果在主线程创建Handler,只需要new Handler()即可,但如果要创建子线程的Handler,那么需要使用另一个构造函数Handler(Looper looper),来传入子线程的Looper对象。
我们在主线程(一般是Activity)中使用Handler,没见到创建主线程,调用Looer.prepare()、loop(),这是由于启动应用的时候,系统已经为程序创建了主线程并调用了Looper的prepare和loop方法,在ActivityThread的main方法中,如下:
1 public static void main(String[] args) { 2 Looper.prepareMainLooper(); 3 ... 4 Looper.loop(); 5 }
浙公网安备 33010602011771号