Handler消息分发机制

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线程不安全。为啥不安全,因为AndroidUI线程操作并没有加锁,也就是说可能在非UI线程中刷新界面的时候,UI线程(或者其他非UI线程)也在刷新界面.这样就导致多个界面刷新的操作不能同步,导致线程不安全。如果,我们直接更新UI,但你不能做过多的耗时操作,否则你会使UI卡顿。

 

4.      流程

我们从UI线程(不用加Looper)开始分析消息分发机制

在Activity中 new Handler,然后我们去看看Handler的构造函数

最终它会有两个构造函数,我们只分析下面这个,另一个很简单就不提了,请看下图

第一个if只是用发射获取一些当前类的信息,并打印,不重要。

看mLooper=Looper.myLooper();

这是啥???

我们可以知道myLooper()返回给我们了一个Looper对象。

补充知识:

Handler是管理某个线程(也可能是进程)的循环消息队列,那它是不是要持有threadMessageQueue的对象。而我们看到handler不没有定义thread,定义了MessageQueue,但是我们可以看到下面mQueue=mLooper.Queue,这就是说,handler并不持MessageQueue与thread对象。

那他们是怎么联系的???通过Looper持有thread与MessageQueue对象,然后handler持有Looper就行了。

所以我们可以说Looperhandler绑定了ThreadMessage 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?????

这就简单了,我们只要把当前handlerUI MessageQueue绑定起来就好了。

有几种方法:https://blog.csdn.net/xx_dd/article/details/52888319

下面给出一个用例

在new Handler()的时候我给其传入了一个Looper(context.getMainLooper)为其绑定。

总结:

1.      上面红色字体

2.      从功能上看handler的消息处理。handler的作用为发送与接受消息,MessageQueue为存取数据和锁线程的。Message消息缓存,是一个单链表。Looper起到死循环取数据和为handler提供MessageQueueThread对象。ThreadLocal存储Looper对象,在handler绑定MessageQueue起到了关键作用。

猜你喜欢

转载自blog.csdn.net/silently_frog/article/details/80797918