首页 > 编程知识 正文

安卓的底层,android leakcanary原理

时间:2023-05-04 04:32:35 阅读:108386 作者:1278

前言

阅读本文的前提是了解一些基类,如Handler、Looper和ThreadLocal。 可以在这里搜索源代码的位置。

源代码分析

分析枚举消息方法

布尔型消息(消息msg,long when ) {

if (msg.target==空值) {

thrownewillegalargumentexception (' messagemusthaveatarget.' );

}

if(msg.isinuse () ) )。

thrownewillegalstateexception (msg ' thismessageisalreadyinuse.';

}

同步(this ) {

if(mquitting ) {

illegalstateexceptione=newillegalstateexception (

msg.target ' sendingmessagetoahandleronadeadthread ';

log.w(tag,e.getMessage ),e );

msg.recycle (;

返回假;

}

msg.markInUse (;

msg.when=when;

Message p=mMessages;

布尔新闻工作;

if (p==空||when==0||whenp.when ) {

//New head,wake up the event queue if blocked。

msg.next=p;

mMessages=msg;

needWake=mBlocked;

} else {

//insertedwithinthemiddleofthequeue.usually we don ' thavetowake

//uptheeventqueueunlessthereisabarrierattheheadofthequeue

//andthemessageistheearliestasynchronousmessageinthequeue。

need wake=m blocked p.target==null msg.is asynchronous (;

消息预;

for (; () )。

prev=p;

p=p.next;

if (p==空||whenp.when ) {

黑;

}

if(Needwakep.isasynchronous () ) )。

needWake=false;

}

}

msg.next=p; //invariant: p==prev.next

prev.next=msg;

}

//We can assume mPtr!=0 because mQuitting is false。

if(needwake ) {

导航(mptr );

}

}

返回真;

}

作为消息队列的内部组织,消息的数据结构实际上是一个链表,按消息when字段的升序排列。 主要看如何将消息链接到链表。

消息分为同步消息和异步消息两种。 一个可以用方法isAsynchronous判断。 如果消息链表开头是障碍,则无法处理该障碍后面的同步消息,但异步消息不受影响。

我们正常使用的时候大部分情况下都是同步消息。 但是,添加屏障的接口是专用API。 这意味着,只要不反射,开发者就无法调用这个API。

现在开始分析代码。 比较重要的代码是以下行

msg.when=when;

Message p=mMessages;

布尔新闻工作;

if (p==空||when==0||whenp.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);

}

如果当前链表为null或者当前消息的触发时间(when)小于链表头的触发时间,那么直接把这个消息加到链表的头部,next指向原头部,如果当前next函数是阻塞的,那么要唤醒next函数。

如果当前链表并且msg的触发时间要比链表头部msg触发时间要大,那么就遍历这个链表,找到它应该在的位置(按when升序)。

这里面有个唤醒next 方法的策略,就是这个needWake变量,如果当前头部的msg是个屏障消息,并且next函数是阻塞的,并且msg是这个链表中触发时间最早的,那么需要唤醒next函数。我们来分析下这几个条件:

如果next没阻塞,那么无论msg是什么,我们都不需要唤醒next。

如果next阻塞,但是链表头部是非屏障消息,说明头部还不到时间执行,到了时间next会自动唤醒,所以手动不需要唤醒。

如果next 阻塞,头部是屏障消息,如果msg不是触发最早的异步消息,那么说明最早的异步消也在等待处理,next函数在指定时间差内会自动唤醒,此时不需要手动唤醒。如果msg是最早的异步消息,那msg后面有可能有异步消息,也有可能没有异步消息,在有异步消息的情况,要重新唤醒next,为了及时能处理当前msg,在没有的情况下也要唤醒next,因为此时next是永久阻塞,阻塞参数为-1。

我们分析下next函数

Message next() {

// Return here if the message loop has already quit and been disposed.

// This can happen if the application tries to restart a looper after quit

// which is not supported.

final long ptr = mPtr;

if (ptr == 0) {

return null;

}

int pendingIdleHandlerCount = -1; // -1 only during first iteration

int nextPollTimeoutMillis = 0;

for (;;) {

if (nextPollTimeoutMillis != 0) {

Binder.flushPendingCommands();

}

//阻塞函数

nativePollOnce(ptr, nextPollTimeoutMillis);

synchronized (this) {

// Try to retrieve the next message. Return if found.

final long now = SystemClock.uptimeMillis();

Message prevMsg = null;

Message msg = mMessages;//链表的头部

if (msg != null && msg.target == null) {//如果头消息是个屏障消息

// Stalled by a barrier. Find the next asynchronous message in the queue.

do {//那么循环找异步消息来处理

prevMsg = msg;

msg = msg.next;

} while (msg != null && !msg.isAsynchronous());

}

if (msg != null) {

if (now < msg.when) {//如果还不到时间执行,那么next会阻塞一个指定时间段

// Next message is not ready. Set a timeout to wake up when it is ready.

nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);

} else {//直接返回给Looper

// Got a message.

mBlocked = false;

if (prevMsg != null) {

prevMsg.next = msg.next;

} else {

mMessages = msg.next;

}

msg.next = null;

if (DEBUG) Log.v(TAG, "Returning message: " + msg);

msg.markInUse();

return msg;

}

} else {//如果没找到消息,那么next可能会陷入阻塞

// No more messages.

nextPollTimeoutMillis = -1;

}

// Process the quit message now that all pending messages have been handled.

if (mQuitting) {

dispose();

return null;

}

// If first time idle, then get the number of idlers to run.

// Idle handles only run if the queue is empty or if the first message

// in the queue (possibly a barrier) is due to be handled in the future.

if (pendingIdleHandlerCount < 0

&& (mMessages == null || now < mMessages.when)) {

pendingIdleHandlerCount = mIdleHandlers.size();

}

if (pendingIdleHandlerCount <= 0) {//如果没空闲任务做,那么next就会陷入阻塞,nextPollTimeoutMillis=-1的话,就永远阻塞。

// No idle handlers to run. Loop and wait some more.

mBlocked = true;

continue;

}

if (mPendingIdleHandlers == null) {

mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];

}

mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);

}

// Run the idle handlers.

// We only ever reach this code block during the first iteration.

for (int i = 0; i < pendingIdleHandlerCount; i++) {

final IdleHandler idler = mPendingIdleHandlers[i];

mPendingIdleHandlers[i] = null; // release the reference to the handler

boolean keep = false;

try {

keep = idler.queueIdle();

} catch (Throwable t) {

Log.wtf(TAG, "IdleHandler threw exception", t);

}

if (!keep) {

synchronized (this) {

mIdleHandlers.remove(idler);

}

}

}

// Reset the idle handler count to 0 so we do not run them again.

pendingIdleHandlerCount = 0;

// While calling an idle handler, a new message could have been delivered

// so go back and look again for a pending message without waiting.

nextPollTimeoutMillis = 0;

}

}

把关键解析写在注释里了。核心的意思,如果头部是空或者头部是屏障消息并且后面没有异步消息,那么next陷入永久阻塞,否则的话,直接处理头部消息,如果头部消息还不到时间处理,那么next会被阻塞(when-now)这么久,否则的话,直接返回给Looper

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。