从 sendMessage 到 handleMessage探寻 Handler 源码

Handler

从 sendMessage 到 handleMessage

跟着我的节奏 按下 Ctrl,鼠标点击 Handler,开始快乐! Skr!!

先找到我们最常用的方法 sendMessage(message)

它直接调用了 sendMessageDelayed(message, delayMillis)

然而 sendMessageDelayed 也只是做了一点校验操作(针对delayMillis的非负校验)

然后 它 直接调用了 sendMessageAtTime(message,SystemClock.uptimeMillis() + delayMillis);

sendMessageAtTime:重头戏开始了

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);
}

可见,由该方法开始,MessageQueue(消息队列)出现了

创建出消息队列之后,把消息和延迟时长都传进去。

Handler中的 enqueueMessage:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    
    
    msg.target = this;// Message 注入 handler
    if (mAsynchronous) {
    
    // {true:异步;false:同步}
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

MessageQueue是一个根据时间排序的 优先级队列

MessageQueue中的 enqueueMessage:

boolean enqueueMessage(Message msg, long when) {
    
    
        if (msg.target == null) {
    
     // 校验 handler
            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) {
    
    //根据 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;
    }

MessageQueue 时间优先级队列 新消息插入图:

优先级队列

从sendMessage到MessageQueue的路线打通了,接下来看看 Looper 这条线。

sendMessage是往消息队列里加新消息,Looper是循环消耗(处理)消息。

Looper的loop()方法主要做两件事:(首先声明,Looper 是一个死循环)

  • 把消息从消息队列中取出来。

    Message msg = queue.next(); // might block ( next()可能阻塞 )
    

    MessageQueue 的 next()方法,就是取出下一个消息(message)

  • 调用Handler的dispatchMessage(msg)的方法去执行message

    msg.target.dispatchMessage(msg);
    

    Handler的 dispatchMessage 方法:

    public void dispatchMessage(Message msg) {
          
          
        if (msg.callback != null) {
          
          
            handleCallback(msg);
        } else {
          
          
            if (mCallback != null) {
          
          
                if (mCallback.handleMessage(msg)) {
          
          
                    return;
                }
            }
            handleMessage(msg);//就是我们重写的方法。
        }
    }
    

    最后调用 handleMessage(msg)方法:就此形成闭环。 大赞!!!

问题追寻

Looper是在那初始化的呢?

Looper的初始化:

首先看Looper的构造方法:(居然是私有的,说明不是其他类启动的Looper)

private Looper(boolean quitAllowed) {
     
     
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

Looper有自己的启动类方法:prepare(true)

private static void prepare(boolean quitAllowed) {
     
     
    if (sThreadLocal.get() != null) {
     
     
        //如果发现对应的threadLocal(key) 已经 有对应的Looper(value)了,抛出异常。
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

Looper 有了,那动力(thread)何来呢?

让我们继续探寻!(点进ThreadLocal.set()方法)

public void set(T value) {
     
     
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);// 获得当前线程的 threadLocalMap(用来保存线程上下文的)
    if (map != null)
        map.set(this, value);// 将此 threadLocal 与 looper 对象绑定到上述 map 中去。
    else
        createMap(t, value);
}

getMap方法就是返回对应线程的 threadLocals 成员变量。

ThreadLocalMap getMap(Thread t) {
     
     
    return t.threadLocals;
}

这样就完成了绑定。

一个线程 对应 一个 ThreadLocalMap,里面的存放着ThreadLocal(key):Looper(value)一对映射

Handler会造成内存泄露的问题,为什么呢?怎么解决呢?

当我们使用Handler时,一般都会采用匿名内部类的方式来重写 handleMessage() 方法。

就是这个匿名内部类的使用导致了内存泄露。

那么其他内部类会导致内存泄露吗?为什么?

这个问题说到后面自然明了。

先说Handler的内部类为什么会导致内存泄露。

  • 从Java语法的层面出发,非静态(static)内部类会 持有 一个外部类的对象(this)。

    正式这样,内部类才可以直接访问外部类的成员属性。

  • 由Handler的执行机制我们可以发现 message 会 和 handler 进行绑定,随后进入到MessageQueue中去。

  • 而MessageQueue中的Message有可能是延迟消息(delay),message可能会存活相当一段时间。

  • 而在message存活的这一段时间内,handler持有的 this 也不能被释放,尽管这个页面已经执行了销毁。

  • 从GC的回收机制来看,有着强引用的对象是不会轻易回收,JVM宁愿抛出异常。

由此可见,单纯的内部类是不会造成内存泄露的,是因为Handler独特的运行机制,才导致了这个问题。

那我们该如何解决呢?

  • 从Java语法的角度触发,非静态内部类会持有外部类对象,而静态外部类却不会,我们把Handler对象搞成静态的就可以了。
  • 在分析中,我们提到了强引用不好回收,相应的,我们可以利用弱引用(WeakReference),这样必要的时候,JVM是可以回收 外部类对象的(当我们要用外部类对象时,还可以去ReferenceQueue中拿)。

为什么我们在主线程中 new Handler 就可以直接用?并没有看到跟Looper的关系。Why?

当我们在 Activity 中使用 Handler 时。

final Handler handler =  new Handler(){
     
     
    @Override
    public void handleMessage(Message msg) {
     
     
        super.handleMessage(msg);
    }
};

从始至终我们都没关心 Looper 在哪准备的,就这样用了,能用就说明一定有地方准备了。

我们知道Android是一个基于事件触发的框架。

他要想做点什么事,总得先来点刺激。

那么刺激APP开始运行的事件:应该就是我们点击桌面上的APP图标了。

这个过程到底发生了什么。
在这里插入图片描述

明确几点:

  • Android主界面(桌面)也是一个APP,通常将其称作 launcher
  • Zygote是Android系统创建新进程的核心进程,负责启动Dalvik虚拟机,加载一些必要的系统资源和系统类,启动system_server进程,随后进入等待处理app应用请求
  • Application是虚拟机(Dalvik/Art)的一个实例。每个进程只有一个虚拟机,每个进程只有一个Application实例。单进程App只有一个Application实例,多进程App有多个Application实例
  • 每个程序都有自己的程序入口,APP的程序入口是 ActivityThread 类里 main 方法。

我废这么多话其实就想引出一点,APP的程序入口在 ActivityThread 里,这里面一定有秘密。

ActivityThread 类 的main 方法局部:

public static void main(String[] args) {
    
    
    
    //...被忽略的代码段...

    Looper.prepareMainLooper();
    
    //...被忽略的代码段...
    
    if (sMainThreadHandler == null) {
    
    
        // 获得主线程的 handler
        sMainThreadHandler = thread.getHandler();
    }

    //...被忽略的代码段...
    
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

可见,在APP的入口处,我们找到了 Looper 的 初始化。

Looper.prepareMainLooper:

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    
    
    prepare(false);// 创建 Looper 对象,并与主线程绑定
    synchronized (Looper.class) {
    
    
        if (sMainLooper != null) {
    
    
            //同样,针对主线程的 Looper 也只准有一个
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

就此我们发现,其实这一切在冥冥之中该有的都有。

并且我们顺藤摸瓜 发现了 H 类,(按住Ctrl点进了 getHandler方法)

class H extends Handler

这个 H 继承了 Handler 类,并重写了很多方法,这说明 针对主线程的 Handler 还是有很大区别的。

H类:handleMessage方法局部:
在这里插入图片描述

细看之后发现不得了,这里面处理的情况似乎都是非常重要的组件动作。

  • 尤其这个 退出APP(EXIT_APPLICATION)的操作,居然就是 quit 掉了主线程 的Looper。

居然可以操作 APP 的退出,这说明 Android 的处理机制很可能就是 Looper + Handler 模式。

如果我们在子线程里搭建了Handler机制,该怎么结束他呢?

why:(不然一直死循环,线程永远不会结束,资源永远不会释放)

我们知道 Looper 的 loop() 方法就是个死循环,当 MessageQueue 中没有消息时,就会阻塞。

Message msg = queue.next(); // might block 

MessageQueue.next() 循环内局部代码:

for (;;) {
     
     
    if (nextPollTimeoutMillis != 0) {
     
     
        Binder.flushPendingCommands();
    }

    //当 nextPollTimeoutMillis 为 -1 时,此方法将从 native 层持续阻塞,直到有事件发生
    //如果 nextPollTimeoutMillis 为 0 时,则无需等待,直接返回。
    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) {
     
     
           //...被忽略的代码段...
        if (msg != null) {
     
     
            //...被忽略的代码段...
        } else {
     
     
            // No more messages. 当没有消息时
            nextPollTimeoutMillis = -1;
        }

        // Process the quit message now that all pending messages have been handled.
        // 处理完所有挂起的消息后,立即处理退出消息。 
        if (mQuitting) {
     
     // 在调用 quit()
            dispose();
            return null;//返回一个 null ,让loop()跳出循环。
        }

        //...被忽略的代码段...
}

Looper.loop() 循环内局部代码:

for (;;) {
     
     
    Message msg = queue.next(); // might block 
    if (msg == null) {
     
     //当 next 方法 返回一个 null 时,跳出循环。
        // No message indicates that the message queue is quitting.
        return;
    }
	//...被忽略的代码段...
}

由上述代码我们看出,要想让loop()跳出循环,需要两步:

  • 唤醒线程,让其继续执行

    nativePollOnce很可能已经让线程阻塞了,需要调用nativeWake将其唤醒。

  • 让 mQuitting = true,这样loop() 这边会 进入return 分支。

当调用 quit 时,MessageQueue.quit():

void quit(boolean safe) {
     
     
    if (!mQuitAllowed) {
     
     
        //主线程不允许自主 quit
        throw new IllegalStateException("Main thread not allowed to quit.");
    }

    synchronized (this) {
     
     
        if (mQuitting) {
     
     
            return;
        }
        //将其设置为 true ,为了next() 方法 返回一个 null,进而让 loop 跳出循环
        mQuitting = true;

        //将 Message对象 的空间都置空。(Message的创建和维护采用了 享元设计模式)
        if (safe) {
     
     
            removeAllFutureMessagesLocked();
        } else {
     
     
            removeAllMessagesLocked();
        }

        // We can assume mPtr != 0 because mQuitting was previously false.
        nativeWake(mPtr);//将线程唤醒
    }
}

就此,loop跳出循环,此handler动作就此结束!

猜你喜欢

转载自blog.csdn.net/weixin_43415201/article/details/119912147
今日推荐