当前位置:网站首页>Further exploration of handler (I) (the most complete analysis of the core principle of handler)
Further exploration of handler (I) (the most complete analysis of the core principle of handler)
2022-06-27 03:26:00 【AD calcium milk Lalalala】
I wrote it a long time ago Handler Related articles of , Now look back , The understanding is still relatively shallow . So , I decided to do another in-depth study Handler.
Let's start with a question : What are the communication methods from the sub thread to the main thread ? What is the principle of sub thread to main thread communication ?
You may answer :RxJava,Handler,EventBus, radio broadcast . But the essence behind these appearances is a set of mechanisms , Namely Handler. You can say that ,Android The core of inter thread communication is Handler.
First let's look at Handler Use , I won't say anything specific , Everyone must be very clear :
Sub thread :handler.sendMessage()
The main thread :handler.handleMessage()
It is probably through this way that inter thread communication is realized , Let's start with the source code , Thinking principle .Handler The source location of is frameworks/base/core/java/android/os below , I'm looking at android9.0 Of . Because I usually study with Mac, So I use the source code tool Sublime Text. We must put down the system source code , Directly from AS Many source codes in the tool are actually hidden .
stay Handler Here we see this line of code :
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}Continue to look at :
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg,
SystemClock.uptimeMillis() + delayMillis);
}Go on and watch :
public boolean sendMessageAtTime(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);
}The key is coming. , We are sendMessageAtTime There is a data structure :MessageQueue. Message queue . Let's take a look at the data structure along the source code :
Message mMessages;We are Message I saw the maintenance Message, Go in and have a look Messaage Data structure of :
/*package*/ int flags;
/*package*/ long when;
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
/*package*/ Message next;Mesage There is another attribute in it :Message next. Obviously the ,Message Data structure of a linked list . We're going back to sendMessageAtTime Method , It turns out that the final call is enqueueMessage Method :
private boolean enqueueMessage(
MessageQueue queue,
Message msg,
long uptimeMillis){
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}The final call is MessageQueue Of enqueueMessage Method :
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
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;
}Let's focus on this code :msg.target Namely Handler Self object , Obviously, it cannot be empty .msg.isInUse The corresponding method is msg.markInUse, If this message is marked , that msg.isInUse It will return true.mQuitting Marks whether the sending message thread exits , If you quit, there is no need to continue . Of course, the front is not the point , Let's see if-else Inner logic :
- p==null Obviously not , The last two lines of code Message p = mMessage so , Generally, it will not be the first message ( If it is the first message , go if Logic , take msg Assign to mMessages);
- when==0 Set up ? Someone will say :sendMessage It's called in sendMessageDealyed No, it's not 0 Do you ? Take a closer look ,sendMessageDelayed What's in it ,SystemClock.uptimeMillis() + delayMillis, Do you see this code . Then pass it on layer by layer , therefore when A more accurate understanding is the specific time , Not the delay time ;
- when<p.when Set up ? In general, it is not tenable .
Then we just need to pay attention to else Just the code inside ,if Inside, we'll look back . Next, let's look at the linked list msg operation , Let's take out the code separately :
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;That's the code , I guess many people can't understand it , however , Is the core of the core ! We will first p Assign to prev, Obviously this p It's original msg Linked list , And then p.next Assign to p, This time for null 了 , Out of the loop .
Again msg.next assignment p, It's assignment null, And then prev Linked list next Point to msg. This completes the whole process of sending messages , so : The essential logic of message sending is to store the message in MessageQueue Held Message Inside the list .
Let's take a look at Handler Construction :
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}so , Initializing Handler When ,Handler Just like Looper It establishes a relationship . Take a look myLooper Method :
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}We can't help thinking , We can get To a Looper object , So when is this object set Come in ?
Let's look at a class :ActivityThead. This class is in the frameworks/base/core/java/android/app Next .
Be careful Main Method :
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();//1
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();//2
throw new RuntimeException("Main thread loop unexpectedly exited");
}Lots of code , We just need to focus on the tag 1 And tags 2 It's about . Mark 1 Click the code at to have a look :
(Looper The method inside )
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}Continue to look at :
(Looper The method inside )
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}Come here , We already know that ThreadLocal Inside Looper When is the object set It's coming in .
Let's look at the mark 2 It's about , Called Looper Of loop Method .
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();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
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;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
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();
}
}Let's analyze this method :
- adopt myLooper Get the main thread Looper Objects and Looper Object maintained MessageQueue;
- Polling the message queue continuously through an endless loop (queue.next());
- msg.target.dispatchMessage(msg).
Focus on the next step 3 Well , We know msg.target Namely Handler Object itself , So let's look again Handler Of dispatchMessage Method :
public void handleMessage(Message msg) {
}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}diapatchMsg The final call is handleMessage. In this way, the process from sending messages to receiving messages forms a closed loop . Come back to , Look again. :
What are the communication methods from the sub thread to the main thread ? What is the principle of sub thread to main thread communication ?
Give a brief answer : The child thread passes through handler Send a message to the message queue , And through msg.target Flag bit save handler Object reference , Main thread pass Looper.loop Keep polling message queues , And finally call the handler Of handleMessage Method . In fact, the essential problem lies in MessageQueue In the message queue Message Linked list data structure , This one is in the heap memory , Belongs to the thread share .
We often hear Handler Memory leaks , So here comes the question :Handler What is the cause of the memory leak ?
We have analyzed such a long source code , Let's look at the reference chain :
MainActivity <- Handler <-Message(msg.target) <- MessageQueue <- Looper.
We know that as long as the application is live ,Looper All the objects will be there , and Handler yes Acticity The inner class of , Internal classes hold external class references by default , thus , The chain of references will not break . according to GC Reachability algorithm ,MainActivity It won't be recycled , Cause memory leaks . Be careful : A memory leak occurs when a child thread sends a message , The main thread exits before receiving the message Acticity, For example, send a delay news . If the main thread receives a message , Will execute msg Of recycleUnchecked() Method :
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}Will target Set up null, In this way, the reference chain will also be in Handler <- Message It's broken here .
边栏推荐
- ORM cache package for laravel
- Mmdetection uses yolox to train its own coco data set
- 2016Analyzing the Behavior of Visual Question Answering Models
- jmeter分布式压测
- Logarithm
- PAT甲级 1021 Deepest Root
- Sword finger offer 𞓜: stack and queue (simple)
- 手机新领域用法知识
- Super détaillé, 20 000 caractères détaillés, mangez à travers es!
- 455. distribute biscuits [distribution questions]
猜你喜欢

Topolvm: kubernetes local persistence scheme based on LVM, capacity aware, dynamically create PV, and easily use local disk

How does the brain do arithmetic? Both addition and subtraction methods have special neurons, and the symbol text can activate the same group of cell sub journals

GAMES101作业7提高-微表面材质的实现过程

2021:Greedy Gradient Ensemble for Robust Visual Question Answering

JMeter distributed pressure measurement

Flink learning 2: application scenarios

Anaconda3安装过程及安装后缺失大量文件,没有scripts等目录

PAT甲级 1026 Table Tennis

QIngScan使用

Learn Tai Chi Maker - mqtt Chapter 2 (3) reserved messages
随机推荐
Uni-app 之uParse 富文本解析 完美解析富文本!
我是怎样简化开源系统中的接口的开发的?
QIngScan使用
Introduction to stm32
Overview of Tsinghua & Huawei | semantic communication: Principles and challenges
How can e-commerce products be promoted and advertised on Zhihu?
Is the division of each capability domain of Dama, dcmm and other data management frameworks reasonable? Is there internal logic?
Flink learning 2: application scenarios
投资理财产品的钱有保障吗?会不会没有了?
Promise source code class version [III. promise source code] [detailed code comments / complete test cases]
TP5 spreadsheet excel table export
Topolvm: kubernetes local persistence scheme based on LVM, capacity aware, dynamically create PV, and easily use local disk
2022中式面点师(高级)复训题库及在线模拟考试
Docker deploy redis cluster
Mmdetection uses yolox to train its own coco data set
2021:Beyond Question-Based Biases:Assessing Multimodal Shortcut Learning in Visual Question Answeri
ESP8266
[Shangshui Shuo series] day 6
Cs5213 HDMI to VGA (with audio) single turn scheme, cs5213 HDMI to VGA (with audio) IC
学习太极创客 — MQTT(六)ESP8266 发布 MQTT 消息