Handler消息分发机制
Handler消息分发机制
1. 问题:
子线程如何更新UI
顺便解决在子线程中为啥handler要放在Looper中???
2. 资源
https://blog.csdn.net/bboyfeiyu/article/details/38555547
https://blog.csdn.net/guolin_blog/article/details/9991569
https://www.jb51.net/article/93199.htm(高深)
3. 分析
为啥我们要用Handler,因为Android UI线程不安全。为啥不安全,因为Android中UI线程操作并没有加锁,也就是说可能在非UI线程中刷新界面的时候,UI线程(或者其他非UI线程)也在刷新界面.这样就导致多个界面刷新的操作不能同步,导致线程不安全。如果,我们直接更新UI,但你不能做过多的耗时操作,否则你会使UI卡顿。
4. 流程
我们从UI线程(不用加Looper)开始分析消息分发机制
在Activity中 new Handler,然后我们去看看Handler的构造函数
最终它会有两个构造函数,我们只分析下面这个,另一个很简单就不提了,请看下图
第一个if只是用发射获取一些当前类的信息,并打印,不重要。
看mLooper=Looper.myLooper();
这是啥???
我们可以知道myLooper()返回给我们了一个Looper对象。
补充知识:
Handler是管理某个线程(也可能是进程)的循环消息队列,那它是不是要持有thread、MessageQueue的对象。而我们看到handler不没有定义thread,定义了MessageQueue,但是我们可以看到下面mQueue=mLooper.Queue,这就是说,handler并不持MessageQueue与thread对象。
那他们是怎么联系的???通过Looper持有thread与MessageQueue对象,然后handler持有Looper就行了。
所以我们可以说Looper为handler绑定了Thread与Message Queue。
Ok,现在我们应该理解到hander里有个Looper它持有Thread与MessageQueue,至于Thread与MessageQueue是多久创建的我们后面说。我们已经创建好了一个handler,然后就是我们用的时候,调用handler方法sendMessage()
最后跟踪到
到了这里,就是把你发送的消息与当前的handler对象封装在Message中,和当前时间加上延迟更新时间一起传入enqueueMessage(),于是我们就通过这个方法把多线程消息有顺序的存储好了,比较简单不进去细看了。需要注意下要读这个方法请注意Message是一个单链表,我们所有的数据都存在这个缓存单链表中,还要知道mMessages成员变量记录的是单链表的头。而且在这个方法中其用到了synchronized锁,保证了多线程时消息存放时的安全性。
好了,接下来我们就是依次的去取数据。如果我们在主线程(UI线程)中,自己写的代码层只要把数据发给handler,此时你的UI就更新了。但是,我们前面只解释了将多线程有序的缓存起来,怎么UI就更新好了。。。我们接下来说,UI handler在背后默默为我们做的事。。。
我们再补充点知识:
UI线程(主线程)在程序启动时就开始创建了
在Android启动接口中的ActivityThread.main()方法中(不知道请百度)
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();
我们可以看到其实在UI 线程中,系统调用Looper.prepareMainLooper(),
我们可以看到调了prepare();
sThreadLocal是啥???可以看到用ThreadLocal定义的。
ThreadLocal是Thread的成员变量,为Thread存储一些信息
所以sThreadLocal.get()查看UI线程此时有存放信息没,但是此时我们在安卓应用程序刚启动的入口,所以没有数据,于是,我们就给它设置了一个new Looper()
好了 这样就解释了前面的留下来的问题,我们为UIhandler 创建一个Thread与MessageQueue,
回到 ActivityThread.main()截图,接着系统为activity创建主线程线程ActivityThread,获得activity的与UI交互的handler等,注意最后一句Looper.Loop()是不是很熟悉。。这是用来开启无限循环取前面handler存储在Message中的数据的方法。
把方法中重要的截下来了,
前面我们说了我们的数据放在Message这个单链表中,而MessageQueue持有其对象。于是我们只要获得MessageQueue,还记得ThreadLocal,这里面记录了Looper里面包含了当前线程持有的MessageQueue对象,所以 me=myLooper()就是获取Looper,然后拿到MessageQueue去死循环读取里面的数据queue.next(),所以在UI线程中我们只要发生消息,后面UI就更新了。。。因为有个死循环去读取你的数据。。。
如果要了解MessageQueue自己去百度
然后将取出来的消息传给我们new 的handler的dispatchMessage,
最终发送给了我们重写的handleMessage中。
好了,现在我们将在UI线程中用handler分发消息走了一遍,接下来,
解决问题:
在子线程中为啥handler要放在Looper中?
由于handler本身只起发送和接收消息的作用。不具有死循环有序的存储和取出多线程消息的功能,而Looper为其提供了这些功能,即没有处理多线程发生消息的能力。
如何在子线程更新UI?????
这就简单了,我们只要把当前handler与UI 的MessageQueue绑定起来就好了。
有几种方法:https://blog.csdn.net/xx_dd/article/details/52888319
下面给出一个用例
在new Handler()的时候我给其传入了一个Looper(context.getMainLooper)为其绑定。
总结:
1. 上面红色字体
2. 从功能上看handler的消息处理。handler的作用为发送与接受消息,MessageQueue为存取数据和锁线程的。Message消息缓存,是一个单链表。Looper起到死循环取数据和为handler提供MessageQueue与Thread对象。ThreadLocal存储Looper对象,在handler绑定MessageQueue起到了关键作用。