在上一次发版本的时候,明明万事俱备,只等打包了上线了,突然测试同学很慌张的跑过来跟我说,服务器开启了版本更新后,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);
最后结果实际上取不到消息,取消息代码:
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;
}
}
通过以上其实看不出为啥取的时候没有消息,有同学知道的也可以留言,不过还是希望有的同学善良一些吧,写出这样的代码,让别人怎么维护呀呀呀~~~