Handle收不到消息的问题分析

说到Handler,今天遇到一个问题:就是handler 发送了消息,却在handleMessage的地方没有收到消息,甚是奇怪。代码如下:


[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //创建一个Thread,使得 handler 在子线程中执行耗时操作,而不是在主线程中执行。  
  2. HandlerThread handlerThread = new HandlerThread("handler_thread");  
  3. handlerThread.start();  
  4. // 创建一个Handler 用前面创建的 handlerThread.getLooper()  
  5. mHandler = new Handler(handlerThread.getLooper());  

以上代码,是多么正常的实例化mHandler;然后通过 mHandler.sendEmptyMessage(0); 发送一个消息出去,这是又多么简单的一次操作。可结果却是多么的出人意料。因为同样的代码,同样的操作,可程序在某些情况下,却收不到了消息?!无奈,只能去分析下源码,看看为什么没有收到消息?!

 

问题根因跟踪分析:

1、当我们调用 sendEmptyMessage()方法的时候,最终会调用到 Handler.enqueueMessage 方法

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
  2.     msg.target = this;  
  3.     if (mAsynchronous) {  
  4.         msg.setAsynchronous(true);  
  5.     }  
  6.     return queue.enqueueMessage(msg, uptimeMillis);  
  7. }  

2、然后跟踪到 MessageQueue.enqueueMessage方法。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. final boolean enqueueMessage(Message msg, long when) {  
  2.     ........  
  3.     boolean needWake;  
  4.     synchronized (this) {  
  5.         // 此处会判断 是否消息队列已经退出,如果退出返回false。  
  6.         if (mQuiting) {  
  7.             RuntimeException e = new RuntimeException(  
  8.                     msg.target + " sending message to a Handler on a dead thread");  
  9.             Log.w("MessageQueue", e.getMessage(), e);  
  10.             return false;  
  11.         }  
  12.   
  13.         ................  
  14.         }  
  15.     }  
  16.     if (needWake) {  
  17.         nativeWake(mPtr);  
  18.     }  
  19.     return true;  
  20. }  

3、那么mQuitting 什么情况会赋值 true呢?跟踪代码找到如下代码:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. final void quit() {  
  2.     if (!mQuitAllowed) {  
  3.         throw new RuntimeException("Main thread not allowed to quit.");  
  4.     }  
  5.   
  6.     synchronized (this) {  
  7.         if (mQuiting) {  
  8.             return;  
  9.         }  
  10.         mQuiting = true;  
  11.     }  
  12.     nativeWake(mPtr);  
  13. }  

由此可见,当调用 quit的时候会对mQuiting进行赋值true,这样在以后的发送消息的时候,就不会添加到消息队列当中去了。

而官方的sendEmptyMessage定义也证明了这一点。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Sends a Message containing only the what value. 
  3.  *   
  4.  * @return Returns true if the message was successfully placed in to the  
  5.  *         message queue.  Returns false on failure, usually because the 
  6.  *         looper processing the message queue is exiting. 
  7.          (通常情况下不会返回false,除非消息队列被强制退出了) 
  8.  */  
  9. public final boolean sendEmptyMessage(int what)  
  10. {  
  11.     return sendEmptyMessageDelayed(what, 0);  

总结:

通过上面分析,问题的原因就有可能是不合法的调用了 quit方法,搜索项目,发现有些地方,在某些情况下会不和业务逻辑的去执行 quit方法,导致无法处理的消息。再就是可能的一个原因,就是 HandlerThread可能会系统强制回收了。

 

所以,为了安全,我们在调用 发送消息的时候最好做一个判断,如果消息发送不成功则重新创建handler。代码如下:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private void createHandler(){  
  2.     HandlerThread handlerThread = new HandlerThread("handler_thread");  
  3.     handlerThread.start();  
  4.     mHandler = new Handler(handlerThread.getLooper());  
  5. }  
  6.   
  7. private void sendMessage(int what){  
  8.     boolean isOk = mHandler.sendEmptyMessage(what);  
  9.     if (!isOk){  
  10.         //创建创建handler  
  11.         createHandler();  
  12.         isOk = mHandler.sendEmptyMessage(what);  
  13.     }  
  14. }  

原文地址:http://blog.csdn.net/ilygjl/article/details/51137346


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
奇虎360公司开源的Atlas是优秀的数据库中间件,美团点评DBA团队针对公司内部需求,在其上做了很多改进工作,形成了新的高可靠、高可用企业级数据库中间件DBProxy,已在公司内部生产环境广泛使用,较为成熟、稳定。 DBProxy的优点 支持多语言MySQL客户端 读写分离 负载均衡 Slave故障感知与摘除(Master需要MHA等其他联动) 后端连接池 自定义SQL拦截与过滤 流量分组与控制 丰富的监控状态 支持分表(分库版本正在内测中) Client IP限制 DBProxy对Atlas的改进 新增功能点 新增参数 backend-max-thread-running用于指定每个MySQL后台的最大thread running数 thread-running-sleep-delay用于指定在thread running数超过backend-max-thread-running时,客户端连接等待的时间 添加到黑名单中需要满足两个条件:SQL执行的时间和频率 提供了查看、修改、添加、删除黑名单的功能 黑名单管理提供了将黑名单保存到文件以及从文件中Load到内存中的功能 在手动添加黑名单时,只需要将用户的SQL语句输入,在内部自动转化成过滤条件,手动添加时是否生效由参数 manual-filter-flag 来控制,OFF:不生效,ON:立即生效 手动添加与自动添加两种情况下的过滤条件是否生效是分别由不同参数控制,这个要区分清楚。另外,也可以使用 admin 的命令来设置是否开启/关闭某个过滤条件 SQL执行的时间 由参数 query-filter-time-threshold 来指定,如果SQL执行时间超过此值,则满足条件 SQL执行频率 由参数 query-filter-frequent-threshold 来指定,如果SQL执行频率超过此值,则满足条件 频率就是在时间窗口内执行的次数。时间窗口则是由频率阈值和最小执行次数来计算出来的,当时间窗口小于60s时,扩展到60s 参数 access-num-per-time-window 用来指定在时间窗口内的最小执行次数,添加此参数是考虑到执行时间长的SQL在计算频率时同时参考其执行的次数,只有执行一定次数时才去计算其频率。当执行时间与执行频率都满足时条件时,会自动将查询作为过滤项放到黑名单中,加入到黑名单中是否生效由参数 auto-filter-flag 来控制,OFF:不生效,ON:立即生效 黑名单的管理 从库流量配置 指定查询发送到某个从库 参数动态设置(完善show proxy status/variables) 支持save config,动态增加、删除分表 响应时间percentile统计 统计最近时间段DBProxy的响应时间 kill session 支持DBProxy的admin接口kill session操作 backend平滑上下线 支持平滑的backend上下线 DBProxy非root用户启动 使用非root用户启动 admin账号的安全限制 admin账号密码的动态修改及host限制 增加异步刷日志的功能 增加日志线程、异步刷日志,提高响应时间 支持DBProxy平滑重启功能 支持SQL过滤的黑名单功能 支持对于MySQL后台的thread running限制功能 该功能通过在DBProxy内限制每个后台MySQL的并发查询,来控制对应MySQL的thread running数 当发向某个MySQL后台的的并发查询超过某个阈值时,会进行超时等待,直到有可用的连接,其中阈值与超时等待的时间都已经参数化,可以动态配置 set backend offline不再显示节点状态 支持set transaction isolation level 支持use db 支持set option语句 支持set session级系统变量 支持建立连接时指定连接属性 改进连接池的连接管理,增加超时释放机制。当连接池中的空闲连接闲置超过一定时

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值