资讯

精准传达 • 有效沟通

从品牌网站建设到网络营销策划,从策略到执行的一站式服务

android消息队列,实现消息队列

android postDelayed实现

2015-01-18 12:00

创新互联建站坚持“要么做到,要么别承诺”的工作理念,服务领域包括:成都网站建设、网站设计、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的万宁网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!

在android中做延时处理一般用handler.postDelayed()和view.postDelayed(action,delay)来实现,view.postDelayed也是通过handlder.postDelayed来实现的,不过有一些特殊处理的地方。

handler处理延时逻辑是通过发送延时消息来处理的

sendMessageDelayed:

sendMessageAtTime:

将延时消息放入到消息队列中。SystemClock.uptimeMillis()是开机到现在的时间(毫秒),不包括睡眠的时间。不用System.currentTimeMillis()的原因是System.currentTimeMillis()的时间是可以被System.setCurrentTimeMillis修改的,如果被修改了则发生难以想象的后果。

enqueueMessage调用MessageQueue.enqueueMessage(msg,uptimeMillis)

此处会将传入的消息对象根据触发时间(when)插入到message queue中。然后判断是否要唤醒等待中的队列。

. 如果插在队列中间。说明该消息不需要马上处理,不需要由这个消息来唤醒队列。

. 如果插在队列头部(或者when=0),则表明要马上处理这个消息。如果当前队列正在堵塞,则需要唤醒它进行处理。

通过nativeWake方法唤醒队列。

looper执行MessageQueue中的消息:

只是调用了MessageQueue.next()方法。可能会阻塞。

该方法会先调用nativePollOnce阻塞,然后进入死循环。nativePollOnce()的作用类似与object.wait(),使用了native方法来对线程进行精确时间的唤醒,

如果head Message是有延迟而且延迟时间没到的(now msg.when),计算下时间间隔(nextPollTimeoutMillis),设置timeout为两者之差,进入下一次循环。

如果Message无延时或已到达执行时间,则直接返回该Message.

如果当前的Message阻塞住了MessageQueue(pendingIdleHandlerCount = 0),把全局变量mBlocked改为true,在下一个Message放入队时会判断这个message的位置。如果在queue的头部,则用nativeWake唤醒线程。

总结:[msg loop] --调用MessageQueue.next(),若头部的消息需要被执行在返回该消息,send该消息之后继续调用next方法获取下一个消息,若头部的不需要被执行则阻塞住队列,若有等待执行的Message,计算一下剩余时间,继续调用nativePollOnce()阻塞,无限循环到阻塞时间到或者下一次有Message进队。下个消息进队时若不需要延时在放在Queue的头部,否则在放在Queue的中部。

view的延时也是调用了handler的postDelayed.

若view的AttachInfo不为空,则调用AttachInfo中的handler的postDelayed。若AttachInfo为空,则先将action放入RunQueue中。RunQueue为HandlerActionQueue,用来存放view没有handler时的action。

执行action:

还是需要传入一个Handler,利用handler来执行action。当view被关联到window时,会执行该队列中的Action.

AttachInfo是View的一个附加信息存储类,当view被关联到window时会被赋值。

Android消息队列浅析

当面试官问到你消息对列的时候,恭喜你,已经跨过初级,在试探你的中级水平了。

Android的消息循环是参考Windows的消息循环机制来实现的。

消息队列4件套   Message、MessageQueue、Looper、Handler

1、Message 是消息对列的消息实体类,因为消息队列中会存放最多10个Message对象。常用属性 what,是消息体的Tag,用来区分是那个一消息体。

2、 MessageQueue  先进先出”的原则存放消息,将Message对象以链表的方式串联起来。

3、Looper 是MessageQueue的管理者,主线程中是一对一的关系。子线程需要用到消息对列的话就需要经典二人组 。先调用 Looper.prepare()方法,然后再调用Looper.loop();

4、Handler 是封装和处理Message对象的。

通过源码可知消息走向如下

handler.sendMessage()--handler.sendMessageDelayed()--handler.sendMessageAtTime()--msg.target = this;queue.enqueueMessage==把msg添加到消息队列中

[Android源码分析] - 异步通信Handler机制

一、问题:在Android启动后会在新进程里创建一个主线程,也叫UI线程( 非线程安全 )这个线程主要负责监听屏幕点击事件与界面绘制。当Application需要进行耗时操作如网络请求等,如直接在主线程进行容易发生ANR错误。所以会创建子线程来执行耗时任务,当子线程执行完毕需要通知UI线程并修改界面时,不可以直接在子线程修改UI,怎么办?

解决方法:Message Queue机制可以实现子线程与UI线程的通信。

该机制包括Handler、Message Queue、Looper。Handler可以把消息/ Runnable对象 发给Looper,由它把消息放入所属线程的消息队列中,然后Looper又会自动把消息队列里的消息/Runnable对象 广播 到所属线程里的Handler,由Handler处理接收到的消息或Runnable对象。

1、Handler

每次创建Handler对象时,它会自动绑定到创建它的线程上。如果是主线程则默认包含一个Message Queue,否则需要自己创建一个消息队列来存储。

Handler是多个线程通信的信使。比如在线程A中创建AHandler,给它绑定一个ALooper,同时创建属于A的消息队列AMessageQueue。然后在线程B中使用AHandler发送消息给ALooper,ALooper会把消息存入到AMessageQueue,然后再把AMessageQueue广播给A线程里的AHandler,它接收到消息会进行处理。从而实现通信。

2、Message Queue

在主线程里默认包含了一个消息队列不需要手动创建。在子线程里,使用Looper.prepare()方法后,会先检查子线程是否已有一个looper对象,如果有则无法创建,因为每个线程只能拥有一个消息队列。没有的话就为子线程创建一个消息队列。

Handler类包含Looper指针和MessageQueue指针,而Looper里包含实际MessageQueue与当前线程指针。

下面分别就UI线程和worker线程讲解handler创建过程:

首先,创建handler时,会自动检查当前线程是否包含looper对象,如果包含,则将handler内的消息队列指向looper内部的消息队列,否则,抛出异常请求执行looper.prepare()方法。

- 在 UI线程 中,系统自动创建了Looper 对象,所以,直接new一个handler即可使用该机制;

- 在 worker线程 中,如果直接创建handler会抛出运行时异常-即通过查‘线程-value’映射表发现当前线程无looper对象。所以需要先调用Looper.prepare()方法。在prepare方法里,利用ThreadLocalLooper对象为当前线程创建一个Looper(利用了一个Values类,即一个Map映射表,专为thread存储value,此处为当前thread存储一个looper对象)。然后继续创建handler, 让handler内部的消息队列指向该looper的消息队列(这个很重要,让handler指向looper里的消息队列,即二者共享同一个消息队列,然后handler向这个消息队列发送消息,looper从这个消息队列获取消息) 。然后looper循环消息队列即可。当获取到message消息,会找出message对象里的target,即原始发送handler,从而回调handler的handleMessage() 方法进行处理。

- handler与looper共享消息队列 ,所以handler发送消息只要入列,looper直接取消息即可。

- 线程与looper映射表 :一个线程最多可以映射一个looper对象。通过查表可知当前线程是否包含looper,如果已经包含则不再创建新looper。

5、基于这样的机制是怎样实现线程隔离的,即在线程中通信呢。 

核心在于 每一个线程拥有自己的handler、message queue、looper体系 。而 每个线程的Handler是公开 的。B线程可以调用A线程的handler发送消息到A的共享消息队列去,然后A的looper会自动从共享消息队列取出消息进行处理。反之一样。

二、上面是基于子线程中利用主线程提供的Handler发送消息出去,然后主线程的Looper从消息队列中获取并处理。那么还有另外两种情况:

1、主线程发送消息到子线程中;

采用的方法和前面类似。要在子线程中实例化AHandler并设定处理消息的方法,同时由于子线程没有消息队列和Looper的轮询,所以要加上Looper.prepare(),Looper.loop()分别创建消息队列和开启轮询。然后在主线程中使用该AHandler去发送消息即可。

2、子线程A与子线程B之间的通信。

1、 Handler为什么能够实现不同线程的通信?核心点在哪?

不同线程之间,每个线程拥有自己的Handler、消息队列和Looper。Handler是公共的,线程可以通过使用目标线程的Handler对象来发送消息,这个消息会自动发送到所属线程的消息队列中去,线程自带的Looper对象会不断循环从里面取出消息并把消息发送给Handler,回调自身Handler的handlerMessage方法,从而实现了消息的线程间传递。

2、 Handler的核心是一种事件激活式(类似传递一个中断)的还是主要是用于传递大量数据的?重点在Message的内容,偏向于数据传输还是事件传输。

目前的理解,它所依赖的是消息队列,发送的自然是消息,即类似事件中断。

0、 Android消息处理机制(Handler、Looper、MessageQueue与Message)

1、 Handler、Looper源码阅读

2、 Android异步消息处理机制完全解析,带你从源码的角度彻底理解

谢谢!

wingjay

![](;s=460)


当前文章:android消息队列,实现消息队列
文章来源:http://www.cdkjz.cn/article/dssdsds.html
多年建站经验

多一份参考,总有益处

联系快上网,免费获得专属《策划方案》及报价

咨询相关问题或预约面谈,可以通过以下方式与我们联系

大客户专线   成都:13518219792   座机:028-86922220