分析Message源码需要学到:
(1)消息缓冲池相关知识
其实这个消息缓冲池的目的是不需要每次都创建新的message,因为message在app运行过程中使用的会很多,每次都创建资源消耗很大,缓冲最大50个。其实实现很简单,就是用一个单向链表实现的,获取(obtain)和回收(recycle)都是对链表头进行操作的。
(2)消息的分类:同步消息、异步消息和屏障(barrier)消息,下面这篇文章很棒
Handler之同步屏障机制(sync barrier)
分析MessageQueue源码
(1)next是怎么实现阻塞和唤醒的
我们知道Looper.loop()里面有个死循环,通过MessageQueue.next()方法去获取要处理的message,如果获取不到就会block阻塞。
但是,到底是怎么实现block的,在面试的时候,面试官肯定希望面试者能回答出来,而不是只知道阻塞。
我们看看next的源码
307 Message next() { 308 // Return here if the message loop has already quit and been disposed. 309 // This can happen if the application tries to restart a looper after quit 310 // which is not supported. 311 final long ptr = mPtr; 312 if (ptr == 0) { 313 return null; 314 } 315 316 int pendingIdleHandlerCount = -1; // -1 only during first iteration 317 int nextPollTimeoutMillis = 0; 318 for (;;) { 319 if (nextPollTimeoutMillis != 0) { 320 Binder.flushPendingCommands(); 321 } 322 323 nativePollOnce(ptr, nextPollTimeoutMillis); 324 325 synchronized (this) { 326 // Try to retrieve the next message. Return if found. 327 final long now = SystemClock.uptimeMillis(); 328 Message prevMsg = null; 329 Message msg = mMessages; 330 if (msg != null && msg.target == null) { 331 // Stalled by a barrier. Find the next asynchronous message in the queue. 332 do { 333 prevMsg = msg; 334 msg = msg.next; 335 } while (msg != null && !msg.isAsynchronous()); 336 } 337 if (msg != null) { 338 if (now < msg.when) { 339 // Next message is not ready. Set a timeout to wake up when it is ready. 340 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); 341 } else { 342 // Got a message. 343 mBlocked = false; 344 if (prevMsg != null) { 345 prevMsg.next = msg.next; 346 } else { 347 mMessages = msg.next; 348 } 349 msg.next = null; 350 if (DEBUG) Log.v(TAG, "Returning message: " + msg); 351 msg.markInUse(); 352 return msg; 353 } 354 } else { 355 // No more messages. 356 nextPollTimeoutMillis = -1; 357 } 358 359 // Process the quit message now that all pending messages have been handled. 360 if (mQuitting) { 361 dispose(); 362 return null; 363 } 364 365 // If first time idle, then get the number of idlers to run. 366 // Idle handles only run if the queue is empty or if the first message 367 // in the queue (possibly a barrier) is due to be handled in the future. 368 if (pendingIdleHandlerCount < 0 369 && (mMessages == null || now < mMessages.when)) { 370 pendingIdleHandlerCount = mIdleHandlers.size(); 371 } 372 if (pendingIdleHandlerCount <= 0) { 373 // No idle handlers to run. Loop and wait some more. 374 mBlocked = true; 375 continue; 376 } 377 378 if (mPendingIdleHandlers == null) { 379 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; 380 } 381 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); 382 } 383 384 // Run the idle handlers. 385 // We only ever reach this code block during the first iteration. 386 for (int i = 0; i < pendingIdleHandlerCount; i++) { 387 final IdleHandler idler = mPendingIdleHandlers[i]; 388 mPendingIdleHandlers[i] = null; // release the reference to the handler 389 390 boolean keep = false; 391 try { 392 keep = idler.queueIdle(); 393 } catch (Throwable t) { 394 Log.wtf(TAG, "IdleHandler threw exception", t); 395 } 396 397 if (!keep) { 398 synchronized (this) { 399 mIdleHandlers.remove(idler); 400 } 401 } 402 } 403 404 // Reset the idle handler count to 0 so we do not run them again. 405 pendingIdleHandlerCount = 0; 406 407 // While calling an idle handler, a new message could have been delivered 408 // so go back and look again for a pending message without waiting. 409 nextPollTimeoutMillis = 0; 410 } 411 }看上面的代码我们知道next中也有一个死循环,有一行很重要的代码nativePollOnce(ptr,nextPollTimeoutMillis),这个代码很重要,看方法名,我们就知道这是一个C代码,需要用到本地调用,我们先给结论:next方法就是通过nativePollOnce实现阻塞的。
nextPollTimeoutMillis值为-1的话,表示无限阻塞,值为0的话,表示立即返回,值为正的就是等待多久。
代码拆开来看,看下面关键的代码:
326 // Try to retrieve the next message. Return if found. 327 final long now = SystemClock.uptimeMillis(); 328 Message prevMsg = null; 329 Message msg = mMessages; 330 if (msg != null && msg.target == null) { 331 // Stalled by a barrier. Find the next asynchronous message in the queue. 332 do { 333 prevMsg = msg; 334 msg = msg.next; 335 } while (msg != null && !msg.isAsynchronous()); 336 } 337 if (msg != null) { 338 if (now < msg.when) { 339 // Next message is not ready. Set a timeout to wake up when it is ready. 340 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); 341 } else { 342 // Got a message. 343 mBlocked = false; 344 if (prevMsg != null) { 345 prevMsg.next = msg.next; 346 } else { 347 mMessages = msg.next; 348 } 349 msg.next = null; 350 if (DEBUG) Log.v(TAG, "Returning message: " + msg); 351 msg.markInUse(); 352 return msg; 353 } 354 } else { 355 // No more messages. 356 nextPollTimeoutMillis = -1; 357 }msg!=null&&msg.target == null,这个消息说明是一个barrier消息,如果消息列表的头是一个barrier消息,之后的同步消息就不会执行,执行asynchronous消息才会被执行。也就是说如果链表头是一个遍历寻找asynchronous消息,或许能找到,也可能找不到,如果链表头是是个正常的同步消息,链表头也可能是null,所以msg为空的话,就说明没有能处理的消息nextPollTimeoutMillis就会被置为-1,如果找到了能处理的msg,还分两种情况,如果这个msg的执行时间到了就返回这个msg,返回之前把这个msg中链表中拿下来,next方法返回,如果这个msg的执行时间还没到,nextPollTimeoutMillis就会被置为时间差,下一次循环调用nativePollOnce就会被阻塞起来。
but,官方对这段代码进行了优化,提高系统效率,利用消息队列空闲时通过Idle Handler做一些事情,比如gc,也就是说又让线程唤醒了,看下面这个设置和注释
407 // While calling an idle handler, a new message could have been delivered 408 // so go back and look again for a pending message without waiting. 409 nextPollTimeoutMillis = 0;阻塞的底层实现:
linux管道pipe详解
Linux epoll模型详解及源码分析Android中的Looper与epoll
参考:
Android 异步消息处理机制前篇(二):深入理解Message消息池
Android多线程分析之四:MessageQueue的实现
Android异步消息处理机制源码剖析