记录一次Handler事故

在上一次发版本的时候,明明万事俱备,只等打包了上线了,突然测试同学很慌张的跑过来跟我说,服务器开启了版本更新后,app无法进入到主页。二话没说我就双击shift进入到相关代码,查看问题,这一套操作,一切都心云流水。无奈找了很久就是不知道问题出在哪,当时真是紧要关头,又出了这一出,手心都冒汗了。还好最后时刻找出了根源所在。就是handler.

 private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            if (msg.what == 1){
                mHandler.removeMessages(1);
                if (mHandler.hasMessages(2)){
                    Log.d(TAG, "handleMessage: 任务还存在");
                }else {
                    Log.d(TAG, "handleMessage: 队列已不存在任务");
                }
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        mHandler.sendEmptyMessageDelayed(1,3000);


        mHandler.sendEmptyMessageDelayed(2, 1000 * 60 * 60 * 24 * 30);
    }

上面这段代码再熟悉不过了,可能第一眼就会觉得处理消息的时候会走if而不是else,实际上会让我们大跌眼镜,实际会走else。在这里也劝各位同学心存善良,不要瞎写。在翻看Handler源码的时候,其实并不能找出问题。
下面是handler类源码,一路跟踪下去,我们发现最后调用了MessageQueue中的enqueueMessage方法:

 public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }



   public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

MessageQueue中的enqueueMessage方法:

 //将message加入队列
    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        synchronized (this) {
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }

            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //加入到队列头部
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                //加入到队列中间
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

看到这里,其实基本知道消息加入了queue中,并且通过日志也可以知道队列确实加入成功了:

 boolean result = mHandler.sendEmptyMessageDelayed(1, 3000);

       Log.d(TAG, "onCreate: 第一条任务加入" + result);

       boolean b = mHandler.sendEmptyMessageDelayed(2, 1000 * 60 * 60 * 24 * 30);

       Log.d(TAG, "onCreate: 第二条任务加入" + b);
log

最后结果实际上取不到消息,取消息代码:

    boolean hasMessages(Handler h, int what, Object object) {
        if (h == null) {
            return false;
        }

        synchronized (this) {
            Message p = mMessages;
            while (p != null) {
                if (p.target == h && p.what == what && (object == null || p.obj == object)) {
                    return true;
                }
                p = p.next;
            }
            return false;
        }
    }

通过以上其实看不出为啥取的时候没有消息,有同学知道的也可以留言,不过还是希望有的同学善良一些吧,写出这样的代码,让别人怎么维护呀呀呀~~~

你可能感兴趣的:(记录一次Handler事故)