线程间的通信机制?为什么要进行线程间通信,同一进程间的多个线程不是共享内存资源吗,还需要进行通信吗?没错,确实需要这样的一个机制。比如,刚刚开始学习Android的时候,我就听别人说在主线程不能做耗时操作,会导致anr,那么,如果子线程的工作做完了并且需要将结果反馈给主线程让其更新UI,这个时候我们就迫切的需要这样的一套机制,来达到线程间通信的目的,也就是Android的Handler机制。
这里,先假设自己没有看过Handler机制的源码,如果让我们来设计这套机制,应该怎么做呢?这套机制该怎么来实现呢?利用面向对象的思想,首先想到的是得有个Message类来封装消息,其次消息得有人来处理,就好比快递包裹得有人来派送,所以得有个Handler类,来扮演处理Message的角色。有了Message和处理Message的Handler,这还不够,因为每个线程一定会接收到很多Message,而且你不确定什么时候会接收到Message,所以搞一个MessageQueue是非常有必要的。那么问题又来了,有了存放Message的MessageQueue,那谁来维护MessageQueue呢,这个角色需要一直"活着",只要线程还在就不允许它挂掉,因为我们不确定线程什么时候会收到消息,当MessageQueue该被处理的消息都已经处理完了,我们可以允许它小睡一会,但是死掉是绝对不可以的。这个辛劳的角色就是我们的Looper。
以上,我们先大致确定了几个重要角色:Message , Handler , MessageQueue , Looper。紧接着要去更具体的去实现它,假设现在有这样的场景,线程A要给线程B发送消息,那么第一步,线程A要把自己的“想法”封装到一个Message对象msg中,这个好说,那么接下来就像我们寄快递会把包裹交给了快递小哥一样,线程A将msg交给了Handler,这里值得我们特别注意,我们寄快递的时候总是特别小心,反复检查目的地的地址是否正确,接收方的电话是否填写正确,同样的,线程A也不能随意的将msg托付给一个“不靠谱”的Handler,所以我们的Handler必须要设置一个“可以确定线程B的属性”,那怎么谁可以用来确定msg是给了线程B呢,谁可以成为线程B的代言人呢?这个点是非常关键的,深思熟虑后,这个角色非辛劳的Looper莫属。如前面所说,Looper要永远活着,永远在“处理”或者“准备处理”MesaageQueue中的Message,这么“伟大且重要”的角色每个线程都是要有的,而且只能有一个,为什么,因为一个就够了哈。说回正题,现在线程A将msg将给了Handler,这个Handler封装了一个线程B的Looper对象,而我们的Looper维护着一个MessageQueue,最终msg进入MessageQueue,等待线程B的Looper来处理它。那么,按照一定的顺序,终于轮到来自线程A的msg了,怎么处理?谁来处理?其实Handler就可以处理,只要给msg封装一个Handler对象就可以,等到线程B的Looper从MessageQueue将msg取出来后,获取到msg封装的Handler,然后让Handler去处理。
至此,总体的一个框架已经有了。但是要去实现它,还有很多问题。
问题1:每个线程都要有且只有一个Looper去接收并且分发Message,换句话说也就是维护MessageQueue,那么怎么实现每个线程有属于自己私有的Looper,彼此隔离互不影响,这就涉及到“线程隔离”的问题。所以我认为ThreadLocal是Handler机制非常重要的一个点。
问题2:还是围绕Looper,Looper可以“睡着”但是绝不可以“死掉”,换句话说,MessageQueue中有可以被处理(或者换个词“分发”更为准确)时,它必须得活着并且去分发Message,当MessageQueue中没有可以被分发的消息时,它可以“睡着”,或者说是“休眠”。那么怎么保证它“长生不老”,“死不掉”呢?没错,那就是死循环,这看起来有点怪,让它死不掉而去死循环。说到这还不明白的话,可以试想,如果不是死循环,你的Looper总有循环结束的时候,那个时候你的线程可能并没有死掉,而别的线程给你发送消息,要跟你通信,怎么办?答案是没办法了,通信失败,因为“伟大”的Looper已经死掉了。想通了“死不掉”的问题,那怎么解决“睡得着”的问题,毕竟总有空闲的时候,在Looper维护的MessageQueue中在一些时候,没有可以被分发的Message,这种时候得让"Looper"“睡得着”,这块会调用jni,让线程休眠。(这块是我的猜想,可能不正确)。
问题3:看了源码后,会发现Message的类型有三种,普通Message,异步Message,屏障Message。这些Message的区别可以在MessageQueue取Message时体现出来,这算是一些细节问题,不展开讨论了。
问题4:一般我们在用Handler时,通常是以内部类的形式,但是如果没有定义为static的话,AS会提示可能会导致内存泄漏,可以从这个点学习下关于内存泄漏,垃圾回收机制,可达性分析等概念。具体就不展开讨论了。
简单总结下:其实Handler机制总体来说并不复杂,涉及的角色(类)并不多,逻辑也比较简单,重点我认为在于实现Looper的线程隔离,以及loop的休眠唤醒机制。当然,Handler机制也并不简单,里面有很多细节的地方值得我们取学习,比如获取和管理Message的方式(享元设计模式),比如休眠唤醒的机制,可以继续向下挖掘,学到更多的东西等等。
本文是站在一个“设计者”的角度去思考和学习Android线程间通信机制(Handler机制),并没有去对代码进行分析,而是去尝试理解其中的设计思路,宏观上去理清整套机制的脉络及流程。如有不对的地方,欢迎大家指正!
网友评论