android-discuss
android-discuss copied to clipboard
[问答]handler底层是如何将消息从子线程发送到主线程的!
handel底层是如何将消息从子线程发送到主线程的!
是把消息添加到一个队列,由handler创建时所处的线程的Looper获取。
取决于Looper,Looper在主线程,就发到主线程了,在子线程就发到子线程了
我的理解是需要把Handler、Message、MessageQueue、Looper这4个类的关系理清。 在程序启动之后会自动在主线程创建一个Looper,称之为MainLooper;同时伴随着一个MessageQueue; 在主线程中创建Handler的时候默认使用的Looper就是MainLooper,故把Handler和Looper关系起来。 在子线程中利用Handler发送消息的时候,一般是Handler从Message中取一个全局的对象,发送到MessageQueue中。接下来在Looper中的for死循环中会从阻塞队列中取到所发送的消息,这个时候就是在主线程中啦。
可以扩展一下,这个问题的实质是handler机制+引用的传递. 理解handler机制有很多方法,我推荐的方法是从四个关键类Handler、Message、MessageQueue、Looper的方法入手来弄清消息传递这一整条线的逻辑: 这条线的走向是:(相关对外提供的方法可以用eclipse或者source insight来查看.) 1产生消息对象 2-->消息相关属性(what,obj,target,callback等)的附加 3-->消息加入到消息队列 4-->Looper对消息队列的循环取出 5-->分发(也就是执行消息内用户定义的处理逻辑) 6-->回收消息对象
从子线程发送到主线程的消息分两种,一种是普通的handler.sendMessage(msg)或定义延时send,另一种是handler.post(Runnable r)或定时或延时post.不管是哪一种,都用到了handler,那么这个handler就会赋值给msg.target. 常见的情况下,handler在主线程中new出来,然后在子线程中拿到它的引用,发送了消息,系统把消息加入到消息队列中,然后就会有一个Looper来从消息队列中取消息.
这个流程中要注意几个对应关系: 一个线程中最多只能有一个Looper,也可以没有.主线程中初始化时已建立Looper.子线程默认没有,如果使用,需要手动创建. 一个Looper对应一个消息队列.也就是说,有几个Looper,就有几个消息队列,
那么一个Message发送时,怎么知道它要加入到哪个消息队列中去? 一个handler持有一个消息队列的引用和它构造时所属线程的Looper的引用. 也就是说,一个handler必定有它对应的消息队列和Looper, 一个线程至多能有一个Looper,也就至多能有一个消息队列. 一个线程中可以有多个handler.
在主线程中new了handler对象后,这个handler对象自动和主线程自动生成的Looper以及消息队列关联上了. 子线程中拿到主线程中handler的引用,发送消息后,消息对象就会发送到target属性对应的那个handler对应的消息队列中去,由对应的Looper来取出处理. 而消息发送到主线程handler,那么也就是发送到主线程的消息队列,用主线程中的Looper轮询.
Looper的loop方法在它关联的线程中执行(比如主线程),但这是一个阻塞式方法,由消息触发后调用循环内部后面部分的handler.dispatchMessage(),实质是调用handler.handleMessage()或runnable.run()执行自定义的逻辑.
Handler负责发送消息,Looper负责接收Handler发送的消息,并把消息回传给Handler自己,messageQueue就是一个存储消息的容器。
话说@l123456789jy 可否把handel改成handler呢。
嘿嘿,为了提高大家伙的交流效率,我建议尽量采用@Trinea的提议,详见README,分享 以[分享]开头;提问 以[问答]开头。
最底层还是通过pipe实现的夸线程吧? 管道就是一个文件,在管道的两端,分别是两个打开文件文件描述符,这两个打开文件描述符都是对应同一个文件,其中一个是用来读的,别一个是用来写的,一般的使用方式就是,一个线程通过读文件描述符中来读管道的内容,当管道没有内容时,这个线程就会进入等待状态,而另外一个线程通过写文件描述符来向管道中写入内容,写入内容的时候,如果另一端正有线程正在等待管道中的内容,那么这个线程就会被唤醒。
@sfshine 线程之间的内存是共享的,线程之间的通信是不需要通过管道的吧?
为什么调用Looper.prepare()就在当前线程关联了一个Looper对象,它是如何实现的。