主线程中有多个handler的情况,只会共用一个消息队列(看代码)

转载:http://www.cnblogs.com/transmuse/archive/2011/05/16/2048073.html

 工作中遇到了这么一种情况,有两个视图,都需要开启异步任务从服务器获取数据,每个view中创建一个Handler,注册到异步任务中去,当异步任务从服务器获取数据出错,或者出现io异常或者http协议异常的时候,使用这个handler通知主视图弹出toast通知用户,在同一个activity中根据条件使用不同的视图,这些视图通过一个栈进行管理,加载A后,创建并注册handlerA个哦taskA,开启异步任务taskA,然后从视图A激活视图B(假定这个时候taskA还没有结束),视图B创建handlerB,注册到异步任务taskB,开启taskB,HandlerA和HandlerB是同一个类的不同实例,taskA请求服务器发生异常,handlerA发送异步消息通知视图A弹出toast通知用户,这个时候视图A处于栈中,没有激活,而视图B处于激活状态,handlerA和handlerB又是同一个类的实例,那么问题来了,HandlerB能够获取HandlerA发送的异步消息吗?过程如下图所示:

   handlerB当然不能接收到taskA中的handlerA发送的message,可能有同学会说了,handlerA和handlerB都是在主线程中创建的handler,他们都关联于主线程,每个线程都有一个队列messageQueue,looper管理这个队列并且分发消息,无论是handlerA还是handlerB都是发送消息到主线程中的messageQueue, 并且这两个handler的代码也是一样的,handlerA所在的视图处于后台,视图B在前台,handlerB应该能够接受handlerA发送的消息并且处理啊,测试一下,果然视图b中虽然没有启动taskB,但是依然弹出了toast,难道这种说法是对的吗?

当然不对,主要有这么两个问题。

  第一个问题:handlerB能否接收到handlerA发送的消息?

  不能,看看Message的创建方式

复制代码
  
  
Message mes = new Message(); mHandler.sendMessage(mes); // ******************************** public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0 ); } // ******************************** public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0 ) { delayMillis = 0 ; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } // ******************************** public boolean sendMessageAtTime(Message msg, long uptimeMillis) { boolean sent = false ; MessageQueue queue = mQueue; if (queue != null ) { msg.target = this ; sent = queue.enqueueMessage(msg, uptimeMillis); } else { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue " ); Log.w( " Looper " , e.getMessage(), e); } return sent; }
复制代码

通过代码我们可以看到Message会指定它的target为发送他的handler

另外一种方式:

复制代码
  
  
Message mes2 = mHandler.obtainMessage(); mes2.sendToTarget(); // ********************************** public final Message obtainMessage() { return Message.obtain( this ); } // *********************************** public static Message obtain(Handler h) { Message m = obtain(); m.target = h; return m; }
复制代码

这两种创建方式都是一样的。

再来看一下消息是怎么分发的?

Looper会不断的轮询消息队列,将消息发送给响应的handler进行处理

复制代码
  
  
public static final void loop() { Looper me = myLooper(); MessageQueue queue = me.mQueue; while ( true ) { Message msg = queue.next(); // might block // if (!me.mRun) { // break; // } if (msg != null ) { if (msg.target == null ) { // No target is a magic identifier for the quit message. return ; } if (me.mLogging != null ) me.mLogging.println( " >>>>> Dispatching to " + msg.target + " " + msg.callback + " : " + msg.what ); msg.target.dispatchMessage(msg); if (me.mLogging != null ) me.mLogging.println( " <<<<< Finished to " + msg.target + " " + msg.callback); msg.recycle(); } } }
//*****************************************************************************
复制代码

  看上面的红色标注的代码,他会调用这个Message的target的dispatchMessage(msg)分发,上面就说过了这个target就是发送这个消息的handler本身.

复制代码
  
  
public void dispatchMessage(Message msg) { if (msg.callback != null ) { handleCallback(msg); } else { if (mCallback != null ) { if (mCallback.handleMessage(msg)) { return ; } } handleMessage(msg); } }
复制代码

至此结束,handlerB根本不会得到handlerA的消息

第二个问题,既然handlerB不能获得handlerA的消息,那么又是如何弹的toast呢?

复制代码
  
  
public static Toast makeText(Context context, CharSequence text, int duration) { Toast result = new Toast(context); LayoutInflater inflate = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null ); TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message); tv.setText(text); result.mNextView = v; result.mDuration = duration; return result; } // ********************************************* public void show() { if (mNextView == null ) { throw new RuntimeException( " setView must have been called " ); } INotificationManager service = getService(); String pkg = mContext.getPackageName(); TN tn = mTN; try { service.enqueueToast(pkg, tn, mDuration); } catch (RemoteException e) { // Empty } }
复制代码
从上面的代码中我们可以看出来,Toast这种机制是不合view相关的,也不和activity相关的,不像dialog,取决于创建它的activity,Toast是由一种称为INotificationManager的服务管理的,所以虽然视图A虽然没有获取焦点,但是视图A对象仍旧在栈中,依旧存在,handlerA对象也存在,所以当他的到消息的时候,他依旧会去处理,弹出toast,Toast是一种很特别的机制,使用的时候一定要小心。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值