Basically all by calling get ()
There are many ways to use Message, whether it's Handler#obtainMessage(), Message#obtain(), or even Handler#postRunnable(), which is essentially a static method to call Message obtain().
public final Message obtainMessage() { return Message.obtain(this); }
public final boolean post(@NonNull Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); ... }
Message() is also public, but get () is more recommended
Get () is not a brainless new instance, but the first out of a Pool that caches messages. It is used frequently in handlers and in complex scenarios where messages are multiplexed to reduce memory consumption and improve efficiency.
public static Message obtain() { synchronized (sPoolSync) { // Synchronization lock added to Pool if (sPool != null) { Message m = sPool; // Return Head, or sPool sPool = m.next; // Update Head before returning m.next = null; // Also point the original next to empty m.flags = 0; // clear in-use flag sPoolSize--; // Decrease the length of the record Pool return m; } } return new Message(); // Newone if no Message has been cached } /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}). */ public Message() { }
Note: Although named Pool, it essentially maintains the Head (sPool) and updates the single-chain list pointed to by the next.
Cache to Pool when recycle
When the message is executed, it is recycle d: specifically, it is cached to the sPool property, and the recorded poolSize is increased, updating the next Message instance pointed to by next before caching.
/*package*/ Message next; public static final Object sPoolSync = new Object(); private static Message sPool; private static int sPoolSize = 0; void recycleUnchecked() { ... synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { // Pool has an upper limit of 50 next = sPool; // Use the previously saved Head as next sPool = this; // Use current Message as Head sPoolSize++; // Self-adding length } } }
Verify the upper limit of Pool size
Messages contain requests, executions, Recycle s, and caches. Handler s are generally more complex to use. Few requests start after one Message has been cached, more often when the previous Message is waiting for execution or execution, and many other messages start requesting again. In this case, the Message in the Pool Will be less and less used, then start to keep new.
Cyclically, the size of a Pool increases, the number of Messagesages stored increases, and the next Message request is picked up individually from the Pool. However, the size of a Pool does not always accumulate, but when recycle finds that the size has exceeded the agreed maximum of 50, the current Message instance is no longer cached, there is no static application, and GC is waiting for it.Recycle.
Let's verify this: for example, send 50 Delay Message s and one Delay older Message to Handler at the same time, print Pool's Size when the first 50 messages are executed, and print Size when the fifty messages are executed. Finally, request another Message to see Pool's Size before and after it runsSituation.
private fun startHandlerThread() { val handlerThread = HandlerThread("Test Handler thread") handlerThread.start() testHandler = Handler( handlerThread.looper ) { msg -> when (msg.what) { 50 -> mainHandler?.post { getMessagePoolSize(msg).let { Log.d("MainActivity", "50 finished and pool size:$it") } } 51 -> mainHandler?.post { getMessagePoolSize(msg).let { Log.d("MainActivity", "51 finished and pool size:$it") } sendMessage(testHandler, 52) getMessagePoolSize(msg).let { Log.d("MainActivity", "52 waiting and pool size:$it") } } 52 -> mainHandler?.post { getMessagePoolSize(msg).let { Log.d("MainActivity", "52 finished and pool size:$it") } } } true } sendMessages(testHandler) sendMessage(testHandler, 51) }
Send code for multiple messages and individual messages, and Pool Size for printing messages.
Note: The Print Size reflection method may be blocked by a non-public API, remember to temporarily close the check of the non-public API by entering a command (adb shell settings put global hidden_api_policy 0) before executing.
private fun sendMessages(handler: Handler) { for (i in 1..50) { Message.obtain().let { it.what = i handler.sendMessageDelayed(it, 2000L) } } } private fun sendMessage(handler: Handler, what: Int) { Message.obtain().let { it.what = what handler.sendMessageDelayed(it, 2500L) } } private fun getMessagePoolSize(message: Message): Int { var size = 0 try { val clazz = Class.forName("android.os.Message") val field = clazz.getDeclaredField("sPoolSize") field.isAccessible = true size = field[message] as Int } catch (e: Exception) { Log.e("MainActivity", "getMessagePoolSize exception:$e") } return size }
Handler has not cached any messages at the beginning, so it has kept 51 instances, cached 50 messages to Pool after the first 50 messages have been executed, and when the fifty-first message is executed, it will no longer be cached inside, so the current Size is fixed at 50. Message requests follow-upIf so, take one from the pool, run out and put it back.
It is also possible to see from the log:
D/MainActivity: Current thread:Thread[Test Handler thread,5,main] Message[1] handle D/MainActivity: Current thread:Thread[Test Handler thread,5,main] Message[2] handle ... D/MainActivity: Current thread:Thread[Test Handler thread,5,main] Message[49] handle D/MainActivity: Current thread:Thread[Test Handler thread,5,main] Message[50] handle D/MainActivity: 50 finished and pool size:50 D/MainActivity: Current thread:Thread[Test Handler thread,5,main] Message[51] handle D/MainActivity: 51 finished and pool size:50 D/MainActivity: 52 waiting and pool size:49 D/MainActivity: Current thread:Thread[Test Handler thread,5,main] Message[52] handle D/MainActivity: 52 finished and pool size:50
summary
It is very rare for a Pool to cache 50 messages in a row, and even if this happens, 50 messages are enough to be reused. In this rare case, a Super-Extreme state of more than 50 messages is requested all at once, plus a new Message. At least this extremely low probability always consumes more memory than the unlimited number of cached messages.Well done!
So get () is more recommended, summarizing:
- Cached Message s can be retrieved from Pool
- You can also cache old Message objects when they are used