Android Handler源码解析以及使用步骤

1:Handler存在的意义,解决了什么?

          众所周知,在android开发当中,主线程不建议做耗时操作(容易阻塞线程导致ANR崩溃掉),子线程不能更新UI,但android其实就是搭建好页面,从服务器当中获取数据,然后把数据展示到页面当中去。与用户进行交互,所以要用到大量的网络请求,而网络请求又是耗时操作,所以只能在子线程中去完成,然后将获取出来的数据发送给主线程,由主线程去更新UI,从而让页面显示,与用户进行交互,所以使用到了Handler,解决了线程间通信的问题。


2:Handler内部的类,以及各自的作用。

            Handler可以分为四个类。    

               (1):Handler:            作用:发送消息和处理消息。

               (2):looper:               作用:1,从消息队列中获取消息 、

                                                                    2,发送给相对应的handler,让handler处理消息。

               (3):message:          作用:消息的载体。

               (4):messagequeue:作用:把消息有序的存储起来。




handler执行流程图

执行流程:(图中有1,2,3..能看懂可以忽略下面讲解)

        从上图可以看到首先是handler发送一条消息(message)到消息队列(messagequeue)中,(补充:因为messagequeue底层为队列,队列的特性为先进先出,所以等下looper获取也会先获取最前进入messagequeue的消息)当message消息存入到messagequeue当中后,looper,消息泵就开始从messagequeue当中无限循环的读取消息,然后发送给handler,让handler处理消息。


  3:looper源码讲解:   

            looper,消息泵,说白了就是一直从消息队列里获取消息,然后发送给handler去做处理。

            looper  类中,最值得关注的地方有两个,一个是perpare方法,一个是loop方法。       

       

Looper的prepare方法。


            prepare详细介绍:

              可以看到首先是一个if语句,判断sThreadLocal是否是空的,是否能从sThreadLocal中取出内容,如果不等于null,也就是说sThreadLocal不为空 的时候,会抛出一个异常,提示一个线程只能有一个looper,如果为空,那就不满足if语句的判断条件,执行下面的代码,也就是sThreadLocal.set(new looper(quitallowd));意思就是创建一个looper对象,保存到sThreadLocal中。这里提示一个,ThreadLocal,可以理解为一个容器,从上图的get和set方法可以看出,能存能取,所以ThreadLocal在这里就是用来保存我们的looper的。所以说这个prepare方法的目的,就是创建looper对象。


               loop详细介绍:    

loop方法开头部分
loop方法循环从消息队列获取消息


最后调用handler的dispatchmessage方法,发送给handler做处理

由于loop源码过于繁琐,截了一些重要图片讲解:

loop方法首先是执行了myLoop方法,获取我们的looper对象,然后走了个if语句,判断我们的looper对象是否存在,如果不存在,就抛出异常,提示没有looper,prepare没有执行。(前面说prepare方法源码的时候已经说到,prepare方法作用就是创建looper对象)如果me(也就是looper对象)不等于null,那么不符合if语句判断要求,继续向下执行,下面是通过我们的looper对象 me 调用了mQueue方法,来获取到消息队列,之后,采用for循环(死循环)里面调用queue.next()方法,获取我们存在消息队列中的消息,之后判断这条消息是否等于null,如果等于null,那么循环终止,调用msg.target.dispatchMessage(msg)方法,将我们的消息一条一条的发送给handler,由handler做处理。如果不等于null,那么循环一直获取消息。

注:第三张图的msg.target.dispatchMessage(msg);

msg.target就是handler,后面讲解handler类中会看到给msg.target赋值。


Handler类讲解:

    handler主要作用就是发送和处理消息。

    首先先看发送消息,sendmessage(message msg),进入这个方法的源码当中,可以看到是直接调用了         sendMessageDelayed(msg,long delayMillis)方法。

sendMessage源码


首先先看方法参数,第一个参数是我们的message,第二个是一个long类型的数值,代表我们的延迟时间,如果是使用sendMessage(msg)来发送消息,那么第二个long类型的参数默认为0,代表及时发送,没有延迟。然后我们继续点进去sendMessageDelayed(Message msg,long delayMillis)方法查看它是怎么处理的。


sendMessageDelayed源码

在这个sendMessageDelayed中,可以看到首先是进行一个if判断,判断我们的long类型的数值是否小于0,如果小于0,那么就让这个long类型的数值(延迟时间)等于0.意思就算说,如果我们发送消息的时候,使用的是handler的sendmessageDelayed来发送消息,第一个参数传入了我们的message对象,第二个参数填写long类型的延迟时间的时候,开发者调皮,输入了一个负数类型的,比如-1000,-2000,也就是一秒之前,两秒之前,你想让这条消息在一秒或者两秒之前发出去???,时空穿梭器??what??!!,所以这个if判断的意思就是这个,如果你调皮,输入的参数小于0,那么就默认等于0,及时发送。从这能看出来,代码的严谨性多重要。言归正传,走完if判断,规定了我们的延迟时间没问题之后,直接return了一个sendMessageAtTime(Message msg,Long uptimeMillis)方法。在上图可以看到最后一小段代码,SystemClock.uptimeMillis()+delayMillis 意思就是当前时间,加上我们规定的延迟时间,就是sendMessageAtTime的第二个参数。我们点进去sendMessageAtTime(Message msg,long uptimeMillis)看一下是做了哪些处理。


sendMessaheAtTime源码

首先可以看到是先获取了MessageQueue消息队列,然后判断了一下这个消息队列是否为空的,如果为空就log打印一个异常。

如果不为空,就调用enqueueMessgae(queue,msg,uptimeMillis)方法,三个参数分别为,刚获取到的消息队列,message对象(发送的消息),还有延迟时间(如果一开始发送使用的是sendMessage,那么默认为0)然后看一下这个方法是怎么处理的。


enqueueMessage源码

首先看到的第一行代码,有没有感觉有些熟悉,这个msg.target是不是在looper.loop方法中用到了。是的,上面说到,loop中的msg.target是handler,就是在这里赋值的,this是本类,当前类又是handler,所以msg.target就是handler对象。然后在;looper.loop()方法中,这个msg.target调用了dispatchmessage方法,来发送消息进行处理。言归正传,下面又是一个if判断,最后return消息队列的enqueueMessage方法,第一个参数是我们发送的消息message,第二个是延迟时间,我们在进去看一下,注意,这个方法是在消息队列中,所有我们需要到messagequeue的类中去找这个方法。(因这个方法源码较多,也涉及到消息队列是怎么先进先出的问题,所以截图分三段)


messageQueue的enqueueMessage源码上半段

                首先先看上半段,一开始就是各种判断,判断我们的msg.target是否是空的,也就是说是否有handler赋值,如果为空就抛异常,判断是否是一个子线程什么什么的。是的话就打印一个异常。个人认为上半段的最重要的就是开头判断msg.target是否为null


messageQueue的enqueueMessage源码中半段
messageQueue的enqueueMessage源码下半段

然后再看最后两个片段,获取我们发送的消息message,还有消息时间等等,最后进行判断是否有消息,时间等等,然后又使用for循环读取其他消息以及时间,最后进行if判断,判断消息的时间,谁时间小,就是往前排队,提前发出,有延迟时间的向后排。所以这个方法的作用就是解释了messageQueue为什么是先进先出。


最后通过handler.handleMessage来处理消息。


Handler使用步骤:

1:在主线程中 通过匿名内部类 创建Handler类对象:


创建Handler匿名内部

2:创建消息对象

    Message message =new Message();    

        //标识

    message.what =1;

    //发送的message消息

    message.obj ="aa";


3:发送消息

        在子线程,调用handler.sendmessage方法,发送消息到主线程


4:在handler匿名内部类重写的handleMessage方法中,进行处理。



注:若上文有哪些内容没有提到,或者有错误,请及时留言更改!

       内容有些多,也请大家不要着急,慢慢看,个人理解的内容已全部说到位

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容