文章将带你理解 Looper、Handler、Message三者的关系。

一、基本用法

延时应用

定时器应用

一个线程发送消息,另外一个线程接收信息

获取主线程

Looper.getMainLooper()是获取主线程消息队列。

取消某个消息

二、原理

Message

\frameworks\base\core\java\android\os\Message.java

Message主要的实例属性是what ,arg1 ,arg2 ,obj,target,callback,next。what是主要是识别身份的,arg1,arg2是int参数,obj是传递的对象的,一般是作为令牌(token)来用,target是所用的handler。callback是Runnable, 需要的时候Run里面的任务。next是下一条message。message之间连成链式连接,这就是消息队列。消息队列是单向链表。
可以看到public static Message obtain()里面会根据原来有没有message来创建,如果有,拿出消息池里面的message出来用,sPoolSize–,如果没有则新建一条。message线程池的大小是50条,MAX_POOL_SIZE = 50。消息池实质是回收消息池,消息数目是可以超过50条的,只是消息池会回收最多50条用完的消息来存着备用。
obtain有好几个重载。

可以根据自己需要创建不同参数的message。
recycle() 是回收message用的。

Handler

post

当用post发送Runnable 时候,它们是进行下面操作。
\frameworks\base\core\java\android\os\Handler.java

消息队列处理
\frameworks\base\core\java\android\os\MessageQueue.java

上面的操作已经把消息打入队列里面了。此时队列是有序排列的。

sendMessage

发送消息
\frameworks\base\core\java\android\os\Handler.java

queue.enqueueMessage是根据放进去的消息来排列,上面已经说了原理。

postDelayed

基本原理和sendMessage一样

\frameworks\base\core\java\android\os\Handler.java

延时在消息从系统启动时间算起,看延时到哪个点,然后放到消息队列里面。

removeCallbacks

移除Callback
\frameworks\base\core\java\android\os\Handler.java

从消息队列里匹对消息然后移除该消息
\frameworks\base\core\java\android\os\MessageQueue.java

handler.removeCallbacksAndMessages(null);是用来清空消息队列的,此时匹对的消息的obj都是为null。

Looper

经过handler把消息压进了消息队列,需要一个循环逐一拿出来用,这个循环是Looper。在调用中用Looper.loop();来开启。

\frameworks\base\core\java\android\os\Looper.java

大致的处理过程是获取当前线程的looper,然后获取looper里面的消息队列,然后一个个处理消息队列里面的消息,处理完就回收消息。

loop()里面的msg.target.dispatchMessage(msg);是调用了我们定义消息处理动作了。dispatchMessage里面是调用handleCallback或者handleMessage。dispatchMessage是Handler里面的方法。
\frameworks\base\core\java\android\os\Handler.java

看下Message msg = queue.next();里面执行了什么。
\frameworks\base\core\java\android\os\MessageQueue.java

MessageQueue.mMessages->( Message并且Message.next)-> ( Message并且Message.next)->…–>null 构成一个单向链表。Message msg = queue.next();是拿出下一条message出来,如果设置了定时,没到时间就会拿出来。拿出来后通过msg.target.dispatchMessage(msg);来执行。这个looper是个循环,有消息就处理,没消息就阻塞,通过epoll机制来调整运行状态。在塞信息进入消息队列适合会判断是否唤醒循环nativeWake(mPtr);

next里面的阻塞代码是下面这个。阻塞是发生在nativePollOnce方法,在native层使用了epoll机制来等待消息。

退出消息队列

\frameworks\base\core\java\android\os\Looper.java

\frameworks\base\core\java\android\os\MessageQueue.java

looper里面的quit()或quitSafely()可以退出消息队列,从代码看出退出队列会把队列清空。


发表评论

电子邮件地址不会被公开。