Android进阶:3、这一次,咱们用最详细的方式解析Android消息机制的源码

决定再写一次有关Handler的源码

Handler源码解析

1、建立Handler对象

使用handler最简单的方式:直接new一个Handler的对象性能优化

Handler handler = new Handler();复制代码

因此咱们来看看它的构造函数的源码:
bash

public Handler() {
        this(null, false);
    }

    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 that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }复制代码

这段代码作了三件事:
架构

一、校验是否可能内存泄漏
二、初始化一个Looper mLooper
三、初始化一个MessageQueue mQueue

咱们一件事一件事的看:
less

一、校验是否存在内存泄漏

Handler的构造函数中首先判断了FIND_POTENTIAL_LEAKS的值,为true时,会获取该对象的运行时类,若是是匿名类,成员类,局部类的时候判断修饰符是否为static,不是则提示可能会形成内存泄漏。
问:为何匿名类,成员类,局部类的修饰符不是static的时候可能会致使内存泄漏呢?异步

答:由于,匿名类,成员类,局部类都是内部类,内部类持有外部类的引用,若是Activity销毁了,而Hanlder的任务尚未完成,那么Handler就会持有activity的引用,致使activity没法回收,则致使内存泄漏;静态内部类是外部类的一个静态成员,它不持有内部类的引用,故不会形成内存泄漏async

这里咱们能够思考为何非静态类持有外部类的引用?为何静态类不持有外部类的引用?

问:使用Handler如何避免内存泄漏呢?
答:使用静态内部类的方式ide

二、初始化初始化一个Looper mLooper

这里得到一个mLooper,若是为空则跑出异常:函数

"Can't create handler inside thread that has not called Looper.prepare() "复制代码

若是没有调用Looper.prepare()则不能再线程里建立handler!咱们都知道,若是咱们在UI线程建立handler,是不须要调用这个方法的,可是若是在其余线程建立handler的时候,则须要调用这个方法。那这个方法到底作了什么呢?咱们去看看代码:
oop

public static void prepare() {
        prepare(true);
    }

    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));
    }复制代码

先取sThreadLocal.get()的值,结果判断不为空,则跑出异常“一个线程里只能建立一个Looper”,因此sThreadLocal里存的是Looper;若是结果为空,则建立一个Looper。那咱们再看看,myLooper()这个方法的代码:
性能

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }复制代码

总上咱们得出一个结论:当咱们在UI线程建立Handler的时候,sThreadLocal里已经存了一个Looper对象,因此有个疑问:
当咱们在UI线程中建立Handler的时候sThreadLocal里的Looper从哪里来的?
咱们知道,咱们获取主线程的Looper须要调用getMainLooper()方法,代码以下:

public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }复制代码

因此咱们跟踪一下这个变量的赋值,发如今方法prepareMainLooper()中有赋值,咱们去看看代码:

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }复制代码

  • 第一步调用了prepare(false),这个方法咱们刚才已经看了,是建立一个Looper对象,而后存到sThreadLocal中;\
  • 而后判断sMainLooper是否为空,空则抛出异常
  • sMainLooper不为空,则sMainLooper = myLooper()

至此sMainLooper对象赋值成功,因此,咱们须要知道prepareMainLooper()这个方法在哪调用的,跟一下代码,就发如今ActivityThread的main方法中调用了Looper.prepareMainLooper();。如今真相大白:
当咱们在UI线程中建立Handler的时候sThreadLocal里的Looper是在ActivityThread的main函数中调用了prepareMainLooper()方法时初始化的
ActivityThread是一个在一个应用进程中负责管理Android主线程的执行,包括活动,广播,和其余操做的类

三、初始化一个MessageQueue mQueue

从代码里咱们看出这里直接调用了:mLooper.mQueue来获取这个对象,那这个对象可能在Looper初始化的时候就产生了。咱们去看看Looper的初始化代码:

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

代码很简单,就是建立了MessageQueue的对象,并得到了当前的线程。

至此,Handler的建立已经完成了,本质上就是得到一个Looper对象和一个MessageQueue对象!

2、使用Handler发送消息

Handler的发送消息的方式有不少,咱们跟踪一个方法sendMessage方法一直下去,发现最后居然调用了enqueueMessage(queue, msg, uptimeMillis),那咱们看看这个方法的代码:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }复制代码

这段代码作了几件事

一、给msg.target赋值,也就是Handler对象
二、给消息设置是不是异步消息。
三、调用MessageQueue 的enqueueMessage(msg, uptimeMillis)方法
咱们只关注第三步:这一步把Handler的发送消息转给了MessageQueue的添加消息的方法。
因此至此,Handler发送消息的任务也已经完成了, 本质上就是调用MessageQueue本身的添加消息的方法!

3、MessageQueue添加消息

MessageQueue的构造函数代码以下:

MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }复制代码

也没作什么特别的事情。咱们去看看enqueueMessage(msg, uptimeMillis)方法代码:

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; }复制代码

代码很长,可是经过观察这段代码咱们发现这个MessageQueue其实是个链表,添加消息的过程其实是一个单链表的插入过程。

因此咱们知道了Handler发送消息的本质实际上是把消息添加到MessageQueue中,而MessageQueue实际上是一个单链表,添加消息的本质是单链表的插入

4、从消息队列里取出消息

咱们已经知道消息如何存储的了,咱们还须要知道消息是如何取出的。
因此咱们要看一下Looper.loop();这个方法:

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;


        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }


            try {
                msg.target.dispatchMessage(msg);
            }

        }
    }复制代码

代码太长我删了部分代码。能够看出这个方法主要的功能是很简单的。

  • 获取Looper对象,若是为空,抛异常。
  • 获取消息队列MessageQueue queue
  • 遍历循环从消息队列里取出消息,当消息为空时,循环结束,消息不为空时,分发出去!

可是实际上当没有消息的时候queue.next()方法会被阻塞,并标记mBlocked为true,并不会马上返回null。而这个方法阻塞的缘由是nativePollOnce(ptr, nextPollTimeoutMillis);方法阻塞。阻塞就是为了等待有消息的到来。那若是在有消息加入队列,loop()方法是如何继续取消息呢?
这得看消息加入队列的时候有什么操做,咱们去看刚才的enqueueMessage(msg, uptimeMillis)方法,发现

if (needWake) {
    nativeWake(mPtr);
}复制代码

当needWake的时候会调用一个本地方法唤醒读取消息。
因此这里看一下消息分发出去以后作了什么?

msg.target.dispatchMessage(msg);复制代码

上面讲过这个target其实就是个handler。因此咱们取handler里面看一下这个方法代码

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }复制代码

代码很是简单,当callback不为空的时候调用callback的handleMessage(msg)方法,当callback为空的时候调用本身的handleMessage(msg)。通常状况下咱们不会传入callback,而是直接复写Handler的handleMessage(msg)方法来处理咱们的消息。

喜欢个人文章的话能够点个赞的哦

写在最后

不少人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,好比学了一段时间感受没有方向感,不知道该从那里入手去学习,对此我整理了一些资料,须要的能够免费分享给你们,后面也会整理比较新比较火的技术分享的flutter—性能优化—移动架构—资深UI工程师 —NDK

若是喜欢个人文章,想与一群资深开发者一块儿交流学习的话,欢迎加入个人合做群Android Senior Engineer技术交流群:925019412

领取方式:Android技术交流群925019412

相关文章
相关标签/搜索