handler机制

我们在面试的时候老是被问,谈谈你对多线程的理解,Handler机制知道吗,有看过源码吗?虽然我这样的学渣在心中默默说了很多。。。但是既然大佬们有求,我们不能拒绝,就乖乖理解学会它完成任务吧!!!有关多线程的理解,可以跳到这里去看看多线程的来龙去脉,下面开始从头开始聊Handler。
大家都知道 handler机制 是依托于4个类的,分别是Handler、Message、Looper、MessageQueue。

  1. 在UI主线程可以使用Handler机制
  2. 在工作线程也就是子线程中也可以使用Handler机制

一:UI主线程中的Handler机制
在日常的子线程耗时操作,UI线程刷新界面中,我们使用Handler并没有做什么特殊的准备工作,就是实例化了一个Handler,重写handlerMessage(),然后我们在子线程发出的消息就能在UI主线程的handlerMessage方法中拿到,很显然,系统为我们做了大量的准备工作,下面我们来看看在Android源码中是怎样实现的。
首先,我们的Android项目是用java语言开发的,那么一个最基础的知识就是程序的入口是在main()函数,Android项目当然也不例外,Android程序的入口是在一个叫做Activitythread类中,这个类中有一个main()函数,与Handle机制相关的代码准备工作也都在这里:

  1. 调用Looper.prepareMainLooper()
  2. prepareMainLooper方法中为mThreadLocal添加了一个Looper的实例对象,这个sThreadLocal在我们的线程中是专门存放变量信息,默认是存储线程相关的对象
  3. 在new Looper()的时候,Looper类的构造器实例化了属于自己的MessageQueue,而且调用Native层的函数拿到当前线程,关联了当前线程与Looper之间的数据绑定
  4. 这个时候,Looper类、MessageQueue类都实例化完成了
  5. 回到最初的main方法中,在调用完Looper.prepareMainLooper()方法后,又去实例化了Handle对象,这个handle对象在源码中是一个H命名的内部类,继承了Handle,这里的Handle内部类作用就是在handleMessage()方法中去处理各种人机交互的操作(触摸点击、)还有比如Activity的生命周期回调等等
  6. 系统通过上述源代码的准备工作,就帮我们开发者创建好了Looper、MessageQueue和Handler这些类,在第3步的时候,当前线程与Looper之间完成了绑定,当然我们还会有一个疑惑,那为什么我们通过Handler发送的消息,就会进入对应的MessageQueue呢?怎么就不会出错?
  7. 当我们在实例化一个Handler的时候(是在UI主线程),Handler的构造器中会获取当前线程的Looper,调用Looper.myLooper(),这个方法会把当前线程的Looper对象传送回来,因为当前线程是UI主线程,所以在程序的入口ActivityThread类中创建好的sThreadLocal中存储的Looper对象return回来,这样就实现了当前线程Handler与Looper之间的数据绑定,那Looper中的消息队列MessageQueue也就拿到了,三者之间实现了数据的绑定。
  8. 让我们再次回到ActivityThread类中的main()函数,Looper、MessageQueue、Handler已经都完成了准备工作,最后 调用Looper的轮询方法,Looper.loop()-----至此Handler机制的准备工作已经完全实现。
  9. 我们再来关注一下Handler是怎么给MessageQueue发消息的?
  10. 当我们通过Handler发消息的时候,不论是哪一个sendMessage方法,最后都会调用到sendMessageAtTime()这个方法(因为现在是在Handler类中,初始化Handler的时候调用Looper.myLooper,拿到了Looper对象,也就是说,我们拿到了当前线程的MessageQueue这个消息队列),在这个方法中会将发送的消息和MessageQueue一起整合作为参数传送给消息队列,让这个消息入队,也就是说存储到MessageQueue中,这样一个Message通过Handler机制就入队了
  11. 消息入队之后,是怎么取出来的?
  12. 我们都知道Handler机制中取消息是靠Looper类中的轮询机制 loop()方法,在loop()方法中,调用myLooper(),获取到当前线程的Looper对象,从Looper中取出和它绑定的MessageQueue,将这个消息队列放入for类型的死循环,然后调用消息队列的next()方法,不断的取出一个个消息,如果没有Message,也就是说MessageQueue空了,没有任务,那么这个死循环return掉,
  13. 继续看MessageQueue.next()方法是怎么做的?
  14. 在next()方法中,也是一个for类型的死循环,这里在取消息的时候,针对不同类型的Message会有三种不同的情况:①空消息,在调用native层的时候,nextPollTimeoutMillis这个参数是传-1,②延时消息,这个参数 = 当前开机时间 - (消息入队的时候存储的开机时间+开发者自己设定的延时时间),③不为空也不是延时的正常消息,这个参数是0
  15. 在next()方法取消息的时候,正常的消息赋值给mMessages参数直接返回给loop(),而延时消息是去对比当前开机时间和设定的延时时间,如果当前时间超过了延时时间,则把这个消息返回,如果没有超过,则跳过这一步,相当于这一次循环直接跑完,由下一次的循环去调用native层,去控制当前线程wait阻塞还是取消息,这样loop方法取消息机制也就完成了
  16. 让我们再次回到loop()方法中,取出消息以后,将这个消息通过Handler对象的target.dispatchMessage消息分发机制返回,这里有2种不同方式,也就是我们在初始化Handler的时候,是直接去重写handlerMessage方法,还是调用callback接口,将消息用对应的方式返回给Handler去处理
  17. 至此,UI线程的Handler机制完全实现

二:工作线程中的Handler机制
工作线程中创建Handler,也就是通常说的子线程使用Handler,通过总结UI主线程Handler我们发现ActivityThread中main()函数为我们做了大量的准备工作,那么在子线程中,这些工作当然要由我们来完成

  1. 在子线程的run方法中,调用Looper.prepare()方法来创建一个当前线程的Looper对象,
  2. 拿到这个Looper对象,也就是Looper.myLooper()
  3. 激活轮询机制,Looper.loop()
  4. 创建Handler对象,这时候当前线程和Looper以及MessageQueue是绑定的,和子线程中的Handler还没有绑定,我们要把第二步拿到的当前线程Looper对象作为参数,传给Handler构造器,这样就绑定了Handler与Looper
  5. 子线程使用Handler就完成了准备工作,但是有一个问题,因为Looper对象是在子线程中拿到的,有可能这个全局变量的Looper值还未赋值,就传给了Handler,这时候就会造成空指针异常,所以Android系统给我们提供了一个专供handler使用的子线程HandlerThread,它是继承于Thread,其实这个线程也没有什么特别之处,就是在HandlerThread类中的run()方法里,获取子线程相关的Looper对象时,用了一个同步方法,然后在调用getLooper()这个方法传给Handler构造器的时候也用了一个同步方法,if去判断,如果当前的Looper对象为空,调用native层wait方法阻塞线程,进行等待,用同步的线程安全性来保证Looper.myLooper()一定可以拿到对象,不至于发生空指针。
    至此Handler的实现机制就讲完了,对照这些步骤,自己追踪源码会很容易理解Handler的机制
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值