zoukankan      html  css  js  c++  java
  • 主线程中有多个handler的情况

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

  • 相关阅读:
    程序员创业第二步:五个角度打造企业世界级竞争力
    开源题材征集 + MVC&EF Core 完整教程小结
    MVC+EF Core 完整教程20--tag helper详解
    MVC5+EF6 入门完整教程13 -- 动态生成多级菜单
    加载驱动三种的方法
    Caused by: javax.el.PropertyNotFoundException: Property [userName] not found on type [java.lang.String]
    Eclipse 中的 insert spaces for tabs 设置方法
    Windows中mysql的配置文件,解决字符集编码问题,统一使用utf8字符集
    ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated ......问题报错解决办法!
    Java中的类三种类加载器+双气委派模型
  • 原文地址:https://www.cnblogs.com/krislight1105/p/3748406.html
Copyright © 2011-2022 走看看