Handler是什么:
handler是Android提供的一套消息处理机制,可以用它来发送和处理消息
Handler的作用:
可以通过handler向主线程发送消息,用来更新UI
可以用它来执行定时任务
也可以用它在不同线程中执行任务
Android为什么要设计只能通过handler机制更新ui:
最根本的目的就是为了解决多线程并发的问题。打个比方,如果在一个activity中有多个线程,并且没有加锁,就会出现界面错乱的问题。但是如果对这些更新UI的操作都加锁处理,又会导致性能下降。出于对性能的问题考虑,Android给我们提供这一套更新UI的机制我们只需要遵循这种机制就行了。不用再去关心多线程的问题,所有的更新UI的操作,都是在主线程的消息队列中去轮询的
Handler的原理:
异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待
那么Android消息机制主要是指Handler的运行机制,Handler运行需要底层的MessageQueue和Looper支撑。其中MessageQueue采用的是单链表的结构,Looper可以叫做消息循环。由于MessageQueue只是一个消息存储单元,不能去处理消息,而Looper就是专门来处理消息的,Looper会以无限循环的形式去查找是否有新消息,如果有的话,就处理,否则就一直等待着
几个关键的类:
Message:Handler接收和处理的消息对象
2个整型数值:轻量级存储int类型的数据
1个Object:任意对象
replyTo:线程通信时使用
what:用户自定义的消息码,让接收者识别消息
MessageQueue:Message的队列
采用先进先出的方式管理Message
每一个线程最多可以拥有一个
Looper:消息泵,是MessageQueue的管理者,会不断从MessageQueue中取出消息,并将消息分给对应的Handler处理
每个线程只有一个Looper
Looper.prepare():为当前线程创建Looper对象
Looper.myLooper():可以获得当前线程的Looper对象
Handler:能把消息发送给MessageQueue,并负责处理Looper分给它的消息
Handler类包含如下方法用于发送、处理消息:
1、sendMessage:
可以发送空消息(只携带what参数)、延时消息、定时消息等
对于延时、定时消息,有时我们可能会想取消消息,这就可以通过removeMessages(int what)、或removeMessages(int what, Object object)、removeCallbacksAndMessages(Object token)将指定消息移除
2、post:
post延时、定时处理Runnable也可以进行取消,可以通过removeCallbacks(Runnable r)、removeCallbacks(Runnable r, Object token)、removeCallbacksAndMessages(Object token)方法进行取消
3、obtainMessage:
mHandler.obtainMessage()生成Message对象,此对象携带其target对象,直接调用sendToTarget方法就可以将该消息发送到mHandler对应的消息队列中,然后在mHandler的handleMessage中进行处理。使用和sendMessage类型,都是发送Message对象。
4、处理消息:
void handleMessage(Message msg):处理消息的方法。该方法通常用于被重写
final boolean hasMessages(int what):检查消息队列中是否包含what属性为指定值的消息
final boolean hasMessages(int what, Object object):检查消息队列中是否包含what属性为指定值且object属性为指定对象的消息
5、创建handler时指定callback来拦截消息:
第一个handlerMessage return true则第二个handleMessage不被调用,若没截获(第一个handleMessage return false)则两个handleMessage先后执行
eg:点击按钮用handler发送消息被Callback截获
自定义线程相关的handler:
HandlerThread的用处:
创建Handler的时候可以指定Looper,所以这个Looper对象可以是别的线程创建的。所以Handler中MessageQueue的轮询不一定非要是创建Handler的线程进行,还可以别的线程进行。但是进行的时候需要我们保证传入的looper对象已经被别的线程创建好了,否则会出现空指针异常。这个时候我们就需要使用HandlerThread这个类来创建这个Looper了。
需要注意的问题:
内存泄漏:
当一个Android应用启动的时候,会自动创建一个供应用主线程使用的Looper实例。Looper的主要工作就是一个一个处理消息队列中的消息对象。在Android中,所有Android框架的事件(比如Activity的生命周期方法调用和按钮点击等)都是放入到消息中,然后加入到Looper要处理的消息队列中,由Looper负责一条一条地进行处理。主线程中的Looper生命周期和当前应用一样长
当一个Handler在主线程进行了初始化之后,我们发送一个target为这个Handler的消息到Looper处理的消息队列时,实际上已经发送的消息已经包含了一个Handler实例的引用,只有这样Looper在处理到这条消息时才可以调用Handler#handleMessage(Message)完成消息的正确处理
在Java中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用。静态的内部类不会持有外部类的引用
解决方法:
要解决这种问题,思路就是不使用非静态内部类,继承Handler时,要么是放在单独的类文件中,要么就是使用静态内部类。因为静态的内部类不会持有外部类的引用,所以不会导致外部类实例的内存泄露。当你需要在静态内部类中调用外部的Activity时,我们可以使用弱引用来处理。另外关于同样也需要将Runnable设置为静态的成员属性。注意:一个静态的匿名内部类实例不会持有外部类的引用
源码相关:
关于Looper:
在线程中使用Handler时必须把它放在Looper.prepare()和Looper.loop()之间。否则会抛出RuntimeException异常。但是为什么要这么做呢?我们来看看源码:
从Looper.prepare()开始
当Looper.prepare()被调用时,发生了什么?
sThreadLocal是个静态的ThreadLocal<Looper> 实例(在Android中ThreadLocal的范型固定为Looper)。就是说,当前进程中的所有线程都共享这一个ThreadLocal。那么,Looper.prepare()既然是个静态方法,Looper是如何确定现在应该和哪一个线程建立绑定关系的呢?
来看看ThreadLocal的get()、set()方法。
Looper.loop()
我们都知道,在Handler创建之后,还需要调用一下Looper.loop(),来看看这个方法是怎么把把消息准确的送到Handler中处理。
从上面的分析可以知道,当调用了Looper.loop()之后,线程就就会被一个for(;;)死循环阻塞,每次等待MessageQueue的next()方法取出一条Message才开始往下继续执行。然后通过Message获取到相应的Handler (就是target成员变量),Handler再通过dispatchMessage()方法,把Message派发到handleMessage()中处理。
这里需要注意,当线程loop起来是时,线程就一直在循环中。就是说Looper.loop()后面的代码就不能被执行了。想要执行,需要先退出loop。
创建Handler
Handler可以用来实现线程间的通行。在Android中我们在子线程作完数据处理工作时,就常常需要通过Handler来通知主线程更新UI。平时我们都使用new Handler()来在一个线程中创建Handler实例,但是它是如何知道自己应该处理那个线程的任务呢?
Handler究竟对Message做了什么?
Handler的post()系列方法,最终调用的都是下面这个方法:
接下来就看看MessageQueue的enqueueMessage()作了什么。
关于MessageQueue
MessageQueue是一个用单链的数据结构来维护消息列表。
可以看到。MessageQueue在取消息(调用next())时,会进入一个死循环,直到取出一条Message返回。这就是为什么Looper.loop()会在queue.next()处等待的原因。
主线程为什么可以直接使用Handler,而不需要Looper.prepare()和Looper.loop()?
根据之前的分析可以知道,主线程中必然存在Looper.prepare()和Looper.loop()。既然如此,为什么主线程没有被loop()阻塞呢?看一下ActivityThread来弄清楚到底是怎么回事。
注意ActivityThread并没有继承Thread,它的Handler是继承Handler的私有内部类H.class。在H.class的handleMessage()中,它接受并执行主线程中的各种生命周期状态消息。UI的16ms的绘制也是通过Handler来实现的。也就是说,主线程中的所有操作都是在Looper.prepareMainLooper()和Looper.loop()之间进行的。进一步说是在主Handler中进行的。
handler用post方式发送消息:
最终也是发送了一个message,将传入的runnable包装在message中(作为它的成员变量callback)
loop拿到message后怎么处理:
拿到message中的handler对象,调用handler的dispatchMessage方法:
总结:
在Android的线程间通信中,需要先创建Looper,就是调用Looper.prepare()。这个过程中会自动依赖当前Thread,并且创建MessageQueue。经过上一步,就可以创建Handler了,默认情况下,Handler会自动依赖当前线程的Looper,从而依赖相应的MessageQueue,也就知道该把消息放在哪个地方了。MessageQueue通过Message.next实现了一个单链表结构来缓存Message。消息需要送达Handler处理,还必须调用Looper.loop()启动线程的消息泵送循环。loop()内部是无限循环,阻塞在MessageQueue的next()方法上,因为next()方法内部也是一个无限循环,直到成功从链表中抽取一条消息返回为止。然后,在loop()方法中继续进行处理,主要就是把消息派送到目标Handler中。接着进入下一次循环,等待下一条消息。由于这个机制,线程就相当于阻塞在loop()这了
来源于之前笔记的整理,有些贴图和内容当时没有记录来源,如果用到,私信我加上~