消息机制——handler
android消息机制——handler
为啥要有消息机制?
android有个主线程,activityThread,可以操作UI,其他线程不能更新UI。APP开发过程中经常遇到耗时操作,如文件读写,网络请求等。如果把这些耗时请求都放在主线程上面,会发生ANR(Application Not Responding)。针对这种情况,一般的解决措施都是另开个线程去执行这些耗时操作,当这些耗时操作执行完,需要更新UI,咋办?这个时候消息机制就可以大显身手,发送message,告诉主线程,去更新UI。
ANR?
三种类型:
- KeyDispatchTimeout(5 seconds) ——按键或触摸事件在特定时间内无响应
- BroadcastTimeout(10 seconds) ——BroadcastReceiver在特定时间内无法处理完成
- ServiceTimeout(20 seconds) ——Service在特定的时间内无法处理完成
两种原因:
- 当前事件没有及时完成
- 当前事件迟迟没有被处理
Handler如何成为通讯员?
ActivityThread被创建时会创建looper,lopper的构造方法里面创建MessageQueue。
- Handler的send与post都会sendMessage,执行enQueueMessage方法,向MessageQueue中插入一条message。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
- looper会不断轮询QueueMessage的next方法。
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
- 发现message,就会执行handler中的dispathMessage,dispathMessage被调用后,调用handlerMessage方法。
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
随便提下
当 MessageQueue中没有message且没有空闲的handler可以处理的时候,通过nativePollOnce将循环阻塞与此。enqueueMessage后,再重新通过nativeWake唤醒。