1:Handler存在的意义,解决了什么?
众所周知,在android开发当中,主线程不建议做耗时操作(容易阻塞线程导致ANR崩溃掉),子线程不能更新UI,但android其实就是搭建好页面,从服务器当中获取数据,然后把数据展示到页面当中去。与用户进行交互,所以要用到大量的网络请求,而网络请求又是耗时操作,所以只能在子线程中去完成,然后将获取出来的数据发送给主线程,由主线程去更新UI,从而让页面显示,与用户进行交互,所以使用到了Handler,解决了线程间通信的问题。
2:Handler内部的类,以及各自的作用。
Handler可以分为四个类。
(1):Handler: 作用:发送消息和处理消息。
(2):looper: 作用:1,从消息队列中获取消息 、
2,发送给相对应的handler,让handler处理消息。
(3):message: 作用:消息的载体。
(4):messagequeue:作用:把消息有序的存储起来。
执行流程:(图中有1,2,3..能看懂可以忽略下面讲解)
从上图可以看到首先是handler发送一条消息(message)到消息队列(messagequeue)中,(补充:因为messagequeue底层为队列,队列的特性为先进先出,所以等下looper获取也会先获取最前进入messagequeue的消息)当message消息存入到messagequeue当中后,looper,消息泵就开始从messagequeue当中无限循环的读取消息,然后发送给handler,让handler处理消息。
3:looper源码讲解:
looper,消息泵,说白了就是一直从消息队列里获取消息,然后发送给handler去做处理。
looper 类中,最值得关注的地方有两个,一个是perpare方法,一个是loop方法。
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方法首先是执行了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)方法。
首先先看方法参数,第一个参数是我们的message,第二个是一个long类型的数值,代表我们的延迟时间,如果是使用sendMessage(msg)来发送消息,那么第二个long类型的参数默认为0,代表及时发送,没有延迟。然后我们继续点进去sendMessageDelayed(Message msg,long delayMillis)方法查看它是怎么处理的。
在这个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)看一下是做了哪些处理。
首先可以看到是先获取了MessageQueue消息队列,然后判断了一下这个消息队列是否为空的,如果为空就log打印一个异常。
如果不为空,就调用enqueueMessgae(queue,msg,uptimeMillis)方法,三个参数分别为,刚获取到的消息队列,message对象(发送的消息),还有延迟时间(如果一开始发送使用的是sendMessage,那么默认为0)然后看一下这个方法是怎么处理的。
首先看到的第一行代码,有没有感觉有些熟悉,这个msg.target是不是在looper.loop方法中用到了。是的,上面说到,loop中的msg.target是handler,就是在这里赋值的,this是本类,当前类又是handler,所以msg.target就是handler对象。然后在;looper.loop()方法中,这个msg.target调用了dispatchmessage方法,来发送消息进行处理。言归正传,下面又是一个if判断,最后return消息队列的enqueueMessage方法,第一个参数是我们发送的消息message,第二个是延迟时间,我们在进去看一下,注意,这个方法是在消息队列中,所有我们需要到messagequeue的类中去找这个方法。(因这个方法源码较多,也涉及到消息队列是怎么先进先出的问题,所以截图分三段)
首先先看上半段,一开始就是各种判断,判断我们的msg.target是否是空的,也就是说是否有handler赋值,如果为空就抛异常,判断是否是一个子线程什么什么的。是的话就打印一个异常。个人认为上半段的最重要的就是开头判断msg.target是否为null
然后再看最后两个片段,获取我们发送的消息message,还有消息时间等等,最后进行判断是否有消息,时间等等,然后又使用for循环读取其他消息以及时间,最后进行if判断,判断消息的时间,谁时间小,就是往前排队,提前发出,有延迟时间的向后排。所以这个方法的作用就是解释了messageQueue为什么是先进先出。
最后通过handler.handleMessage来处理消息。
Handler使用步骤:
1:在主线程中 通过匿名内部类 创建Handler类对象:
2:创建消息对象
Message message =new Message();
//标识
message.what =1;
//发送的message消息
message.obj ="aa";