工作中遇到了这么一种情况,有两个视图,都需要开启异步任务从服务器获取数据,每个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是一种很特别的机制,使用的时候一定要小心。

主线程中有多个handler的情况的更多相关文章

  1. Handler一定要在主线程实例化吗?new Handler()和new Handler(Looper.getMainLooper())的区别?

    一个帖子的整理: Handler一定要在主线程实例化吗?new Handler()和new Handler(Looper.getMainLooper())的区别如果你不带参数的实例化:Handler ...

  2. Android主线程的消息系统(Handler\Looper)

    前言: 之前的文章写的都是关于Bitmap和内存的优化技术,这一篇文章给大家谈谈Handler. Handler是Android系统中比较重要的一个知识,在Android多线程面试经常会被问到,在实际 ...

  3. ThreadLocal ——android消息机制handler在非主线程创建not called Looper.prepare() 错误的原因

    引用自:https://www.jianshu.com/p/a8fa72e708d3 引出: 使用Handler的时候,其必须要跟一个Looper绑定.在UI线程可直接初始化Handler来使用.但是 ...

  4. Android中为什么主线程不会因为Looper.loop()方法造成阻塞

    很多人都对Handler的机制有所了解,如果不是很熟悉的可以看看我 如果看过源码的人都知道,在处理消息的时候使用了Looper.loop()方法,并且在该方法中进入了一个死循环,同时Looper.lo ...

  5. Android:子线程向UI主线程发送消息

    在Android里,UI线程是不同意被堵塞的.因此我们要将耗时的工作放到子线程中去处理. 那么子线程耗时处理后要如何通知UI线程呢? 我们能够在UI主线程中创建一个handler对象,然后通过重写其h ...

  6. Android子线程更新UI主线程方法之Handler

    背景: 我们开发应用程序的时候,处于线程安全的原因子线程通常是不能直接更新主线程(UI线程)中的UI元素的,那么在Android开发中有几种方法解决这个问题,其中方法之一就是利用Handler处理的. ...

  7. Handler详解系列(四)——利用Handler在主线程与子线程之间互发消息,handler详解

    MainActivity如下: package cc.c; import android.app.Activity; import android.os.Bundle; import android. ...

  8. Android 使用handler实现线程间发送消息 (主线程 与 子线程之间)、(子线程 与 子线程之间)

    keyword:Android 使用handler实现线程间发送消息 (主线程 与 子线程之间).(子线程 与 子线程之间) 相信大家平时都有使用到异步线程往主线程(UI线程)发送消息的情况. 本文主 ...

  9. Handler具体解释系列(四)——利用Handler在主线程与子线程之间互发消息

    MainActivity例如以下: package cc.c; import android.app.Activity; import android.os.Bundle; import androi ...

随机推荐

  1. JUnit4 笔记

    1. JUnit4基础 JUnit4 与 JUnit3不同的是,不需要继承测试类,而是通过JDK5提供的注解去标识测试方法. 常用的注解如下: @Before:初始化方法 对于每一个测试方法都要执行一 ...

  2. Jquery获得控件值的方法

    一 Jquery获得服务器控件值的方法 由于ASP.NET网页运行后,服务器控件会随机生成客户端id,jquery获取时候不太好操作,google了下,总结有以下3种方法: 服务器控件代码:<a ...

  3. JavaScript入门(4)

    一.JS能做什么? 1.增强页面动态效果(如:下拉菜单.图片轮播.信息滚动等) 2.实现页面与用户之间的实时.动态交互(如:用户注册.登录验证等) 什么是变量?http://www.cnblogs.c ...

  4. 移动端Web页面问题

    1.安卓浏览器看背景图片,有些设备会模糊.   用同等比例的图片在PC机上很清楚,但是手机上很模糊,原因是什么呢? 经过研究,是devicePixelRatio作怪,因为手机分辨率太小,如果按照分辨率 ...

  5. Unity3D 之2D动画机

    这里来讲解一下2D动画机的使用 2D的时候,默认的情况下,可以调用默认的站立之类的动画,然后通过触发,可以变化自己的动画. 一:将一个图切成一些一个元素 二:创建一个精灵,给精灵添加一个动画机 三:给 ...

  6. MVC中的URL路由(一)

    URL路由系统通过对请求地址进行解析从而得到以目标Controller名称为核心的路由数据.Url路由系统最初是为了实现请求url与物理文件路径分离而建立的,MVC的Url Route是将Url地址与 ...

  7. Android数据存储技术

    Android提供了4种数据存储技术,分别是SharedPreferences.Files.SQLite数据库和网络存储数据.(有的开发者认为使用ContentProvider也可以算是一种,但我觉得 ...

  8. hibernate_validator_01

    1.环境准备(Maven工程) <?xml version="1.0" encoding="UTF-8"?> <project xmlns=& ...

  9. windows server 2008镜像重启后密码变为默认密码的问题的解决方案

    1. cmd中执行regedit,打开注册表: 修改HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Cloudbase Solusions\Cloudbase-Init ...

  10. Ajax--1

    1.Ajax:组合利用javascript.XML和DOM等技术,在无需要刷新页面的前提下实现浏览器与服务器通信.它在用户和服务器之间引入了一个中间层,负责转发用户界面和服务器之间的交互.在服务器处理 ...