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     }

 

posted @ 2020-03-25 14:45  hellodingc  阅读(318)  评论(0)    收藏  举报