从源码角度解析Handler

来源:互联网 发布:网络渗透测试工程师 编辑:程序博客网 时间:2024/06/06 13:25

Handler

.概述

1.每个Handler都和一个创建它的线程及该线程的MessageQueue关联。

2.handler可以用来发送messageRunnable对象到创建handler的线程的MessageQueue里。

3.handler发送消息的方法有 post, postAtTime(Runnable, long), postDelayed, sendEmptyMessage, sendMessage, sendMessageAtTime,  sendMessageDelayed 。

4.主线程默认是有MessageQueue的,而新创建的线程是没有的。

.handler的使用

1.主线程中:

1.直接在主线程中初始化handler,并重写其handlerMessage方法

2.在非主线程中先获取一个Message对象,可以通过handler.obtainMessage()或直接new一个message对象。然后 使用handlersendMessage()方法吧该Message对象发送多handler所在的线程。

3.也可以直接调用handlerpost()方法把一个Runnable抛到handler所在的线程里执行。

4.不过是发送Message还是post一个Runnable,都可以设置delay时间。

2.工作线程中使用Handler步骤如下:

1.Looper.prepare():初始化MessageQueueLooper

2.初始化Handler

3.Looper.loop():进行消息循环,这时候加入死循环,loop()下面的代码不会执行;

4.发送消息跟在主线程里的一样。

.Handler实现原理

1.

子类必须实现的,当有消息来的时候,会回调该函数。

2.

获取消息池里现有的Message,起到对象复用的作用。当然我们也可以自己new一个Message

3.

很多时候我们创建handler的线程和调用handlerpost()方法的线程不是同一个,所以post把一个Runnable任务抛到 handler所在的线程执行。如,我们在UI线程创建了handler,而在自定义线程想修改UI上的一个TextView,我们不能 直接在工作线程里修改,否则会抛出异常,这时候可以创建一个Runnable,在里面修改UI,然后posthandler所在的 线程,即UI 线程。

post方法主要做两件事。1.Runnable整合为Message,因为Handler只能做Message的消息循环调用。看看getPostMessage ()做了什么:

可以看到,该方法先获取一个Message,然后把Runnable设置为该Messagecallback,然后返回该message

然后post方法调用了sendMessageDelayed(),并传进去该messagedelay的时间为0

4.接下来看看sendMessageDelayed()方法,该方法直接调用的是sendMessageAtTime(Messagelong

 

5.

sendMessageAtTime()把message在设定时间入队。对于使用post传递的Message不会回调handleMessage()方法而 是在handler所在的线程自动执行。(见Looper实现原理)

 

.Looper实现原理

0.Looper里面有一个ThreadLocal,记录的是当前线程的Looper

 

1.当调用Looper.prepare()时,调用了重载函数prepareboolean

2.prepareboolean)函数如下

从该函数可以看出,每个线程只能有一个LooperThreadLocal保存的是Looper对象。

该函数当Looper在该线程第一次调用时,会创建Looper对象,并添加在该线程的ThreadLocal里。再看看Looper构造 函数:

3.Looper构造函数如下:

该函数创建了一个MessageQueue,并设置了当前线程。

 

4.当调用Looper.myLooper()时获取的是当前线程的Looper,即当前ThreadLocal里的Looper,所以只有当调用

Looper.prepare()时,ThreadLocal里才有保存当前线程的Looper对象,否则为null

 

5.当调用Looper.loop()时开启消息循环

a.可见loop()方法里面是一个死循环,一直从MessageQueue里取出消息,如果MessageQueue当前为空时会阻塞。

如果调用了Looperquit()方法则MessageQueue会返回null,然后退出死循环结束。

 

b.当有Message取出来时,把消息发送给Message的所有者

消息的所以者是谁呢?

当我们在其他线程要向handler所在的线程发送Message时,可能会调用handler.obtainMessage()来获取一个Message

(这是对Message对象的复用,也可以new一个Message对象,这时候的消息所有者要看sendMessage()方法)

该方法如下:

再看看Message.obtain(boolean)

obtainboolean)方法做两步动作,1.获取一个Message对象,然后返回给调用者。2.把该Messagetarget设置为 调用它的handler,即设置该message的归属者。

c.Message把消息发送给其所有者后

如果该消息的callback不为空,即在使用handler发送消息是使用的是post发送Runnable的,则调用handleCallback()

该方法直接调用messagecallbackRunnable)的run方法。因为该方法是在handler所在的线程调用的,所以run方法 也就是在handler的线程运行。

如果callback为空,即不是通过post发送消息的,则会调用handler的钩子函数handleMessageMessage)。

 

 

6.当调用handlersendMessageMessage)时最后会调用sendMessageAtTime()

 

该方法会把消息入队。再看看enqueueMessage()

该方法会把Messagetargethandler)设置为当前handler,然后把message加入到MessageQueue里。

 

7.当要终止该线程时,记得终止掉消息循环:

1.调用Looperquit()方法,该方法是不安全的,它会终止消息循环,并且不保证MessageQueue里剩下的Message 会被执行。当Looper退出之后再想MessageQueue发送消息就会失败。

2.调用Loop人的quitSafely()。该方法是安全的,它会保证MessageQueue里的Message被执行完,但是之后再再 尝试入队会失败。

.Message实现原理

1.Message的构造函数是public的,所以我们可以直接new一个对象。但是最好的办法是通过Message.obtain() Handler.obtainMessage()来获得一个Message对象。因为obtain方法会先从Message对象池里取出可复用的对象,当没有 对象可用时,才会new一个。这样可以起到复用,节省内存的作用。内部实现如下:

1.

Message内部使用单向链表来储存缓存的Message对象。

next:表示链表的下一个Message

sPoolSync:在多线程获取Message对象时加锁用的。

sPool:缓存链表头。

sPoolSize:缓存链表的当前长度。

MAX_POOL_SIZE:最大缓存数。

2.当我们使用Message.obtain()来获取一个Message对象时:

会先从缓存链表里取出来,如果缓存链表里已经为空了则new一个再返回。

 

2.public  int what:    用户可自定义的辨别数据。

3.public int arg1; public int arg2;         如果只是想通过handler传递简单的int数据,可以使用arg1arg2.

4.public Object obj;         用于传输对象,如果使用Messenger在进程间传输的话,obj对象必需是继承了parcelable 的系统类的对象。

.Handler与内存泄漏问题

见我之前博客的Android内存篇文章

1 0