Handler源码分析

参考郭神博客:https://blog.csdn.net/guolin_blog/article/details/9991569

再讲解Handler前,我先说下写这篇博客的目的。首先一个是Handler非常重要,因为开发必用,很多框架底层都用到Handler,诸如安卓自带的AsyncTask底层就用到Handler,大名鼎鼎的Square公司出的Retrofit网络框架底层也用到Handler。所以说Handler很重要。而研究源码更加重要,研究源码除了对使用有更深刻的了解外,还能学习源码的一些设计思想,让我们不仅仅停留在会用的层次,而可以上升知其然知其所以然的境界。另外写这篇博客的另一个重要原因就是Handler源码几乎是面试必问项,问了有更深刻的理解才整理成博客,也好给自己增添一丝价值。

Handler是用于解决子线程无法刷新UI而诞生的,而Handler这种处理方式叫做异步消息处理机制。看这篇博客的,我默认基本对于Handler的使用烂熟于心了,所以这里不讲解Handler的使用步骤(研究源码其实就涉及到步骤,但不细讲)。讲源码前让我们先看看Handler异步消息处理机制涉及到的几个对象吧(知己知彼,方能百战不殆)


1.Handler

    百度翻译为处理器,说明Handler的职责就是用于处理某些事情的,处理什么事情呢?异步消息处理机制,那肯定就是处理消息啦。或许你有疑问消息从何而来?其实也是通过Handler发送而来(但底层是用Looper发送,待会分析)。所以小总结就是Handler是用于发送和处理消息的一个处理器

2.Message

    代表消息的类,所有要发送给Handler进行处理的消息统统使用这个类封装

3.MessageQueue

    从名字可以清楚获知该对象就是用于存放消息的队列(但其内部并非队列,待会分析),安卓很人性化,考虑到万一我们要发送很多消息,一个Message表示不过来,特意整了一个MessageQueue来存放消息

4.Looper

    谷歌翻译为尺蠖???一脸懵逼怎么起的这个名?其实作用是一个消息轮询器,用于轮询(遍历)MessageQueue取出Message发送给Handler进行处理的

这样子讲不知道大家是否明白了Handler异步消息处理机制的大概流程呢?这里再做一个小总结一句话就是Looper消息轮询器会不断从MessageQueue中取出Message发送给Handler处理(还不理解?凭什么这么说?小二,上源码!)

使用Handler处理消息第一步肯定是先创建Handler了,但这创建其实大有学问,如果你一不小心在子线程创建Handler用来处理消息,那你估计会看到日志给了你一个红色的异常,如下

 new Thread(new Runnable() {
            @Override
            public void run() {
                Handler mHandler = new Handler();
            }
        }).start();
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

此时没看源码的你可能下一步就是百度了,谷歌了等等,然后心里再默念:擦,这才创建个Handler对象,就给老子抛异常?

其实此时如果你不通过百度谷歌,看下源码很快就知道为什么会抛异常了。看下上面的异常信息翻译一下就是不能在线程还没调用Looper的prepare()方法时创建Handler。这异常还是很好翻译的嘛,然后接下来就是自然而然就想着进入Handler构造函数看下究竟做了些啥才会导致的异常吧?看了下源码是先调了空参数的构造函数,该构造函数内部调用了如下这个带两参数的构造函数

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

眼尖的你肯定看到了14行的异常跟我们刚刚上面抛出的异常完全一模一样了吧?那么问题就完美地转为了这个mLooper为什么是null了吧?那肯定就是一不做二不休(什么形容?)点击上面Looper.myLooper()进入看下究竟做了什么导致返回一个null的mLooper啦。代码如下

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

可以看到是调用了sThreadLocal.get()方法获取一个Looper对象,那这个sThreadLocal又是什么呢?其实从名字上就可以看出是一个ThreadLocal对象,如下

// sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

等等,这个sThreadLocal上的注释写的也太好了吧?就是没调用prepare()方法时通过sThreadLocal的get()方法将获取到null!!这不正好跟我们刚刚那个异常信息吻合吗?都出现了prepare()方法,那这个prepare()究竟是何方神圣呢?我们在Looper这个类里面搜索一下,代码如下

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对象不为空,就会抛出异常。从判断和异常信息我们也知道每个线程只能有一个Looper对象。之后就是创建了一个Looper对象然后放进sThreadLocal中。所以prepare()方法也没啥神奇的,就是创建了一个Looper对象放入sThreadLocal中。再回到最初抛异常的地方,是不是好稍微理解为什么会抛出那个异常了?简单讲就是我们没有手动调用Looper中的prepare()方法,导致sThreadLocal中没存放Looper对象,最终造成我们在创建Handler对象时,内部通过Looper调用myLooper从sThreadLocal中取出Looper为null导致抛出的异常。是不是连起来读很绕?很烧脑?那再看看以上逻辑几遍,估计你也能轻松地整出这么一句话的,哈哈。

看到这里有些机制滴小伙伴就纳闷了,我们平时其实很少在子线程创建Handler,而是在主线程创建的,我们也没手动调用Looper的prepare()方法呀,为什么这时候就不抛异常呢?是不是觉得很神奇又费解?其实稍微思考下还是可以猜出个大概的,那就是系统帮我们调用了呀!那是在哪个类帮我们调用了呢?这里我得考考大家个技术问题了,安卓进程的入口是什么呀?有些小伙伴就抢着回答了,我,我,我知道,不就是Activity的onCreate()方法嘛!听到这另一些小伙伴坐不住了,错啦!是Application的onCreate()!哈哈,我想说你们都错啦!看清楚问题哦,我说的是进程的入口,不是应用程序的入口!正确答案是ActivityThread!(具体为什么是这家伙我其实也不是很清楚,看郭神博客知晓滴)确切的说应该是ActivityThread的main()方法是进程的入口。这点其实可以从ActivityThread的源码最上方的文档注释可以瞥见一二,如下

This manages the execution of the main thread in an
 * application process, scheduling and executing activities,
 * broadcasts, and other operations on it as the activity
 * manager requests

从注释知这个类可作为管理器用于管理应用程序进程中主线程的执行,调度和执行Activity,广播接收者以及其它操作。看出它还是很强大的,对它我们就不多废话了,直接搜索main()方法看到如下代码

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

        // 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();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

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

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

首先明确我们滴目的是找到哪里调用了Looper的prepare()方法,大家把目光集中到代码第21行,这里调用了Looper的prepareMainLooper(),看方法名就知道是准备一个主Looper,那跟我们要的prepare()有什么关系呢?估计很多小伙伴都猜到了,那就是这个方法内部就调用了prepare(),不信我们看源码,如下

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()方法,只不过是调用带一个参数的,我们刚刚在上面分析异常也知道最终prepare()方法内部就是调用这个带一个参数的prepare()方法的。所以这里我们就完美了解释了为什么主线程直接创建Handler不抛异常,而子线程不手动调用Looper.prepare()方法时会抛出异常了吧?眼尖的小伙伴还看到上面main方法最后还调用了Looper.loop()方法,然后后面居然直接抛出了异常!从异常信息可知主线程的loop是不能退出的。我们平常也很少去关心这个loop,所以我们可以先大胆猜想这个loop()方法内部应该是执行了一个死循环,这样子循环不止,那个异常就不会抛出来啦!(源码确实就是死循环,待会分析)

废话了这么多,结果就讲解了一个Handler创建导致的问题!不知道小伙伴对于子线程单独创建Handler会抛异常而主线程不会是否有所了解了呢?其实不算太难吧?多看几遍就清楚了,面试也可以吹牛逼啦!这里写一下如果我们遇到需求是在子线程处理消息,该如何创建Handler呢?看到这且理解了上面的讨论的小伙伴可以先自行思考哦,答案如下

  new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Handler mHandler = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        //这里处理接收到的消息msg
                    }
                };
                Looper.loop();
            }
        });

至于上面Looper.loop()是干什么的我们稍后源码分析。

创建完Handler接收处理消息后,接下来的问题肯定就是如何发送消息了,如何发送想必小伙伴们都是信手沾来来,如下

new Thread(new Runnable() {
            @Override
            public void run() {
                //前面是一大堆耗时的操作...
                Message message = mHandler.obtainMessage();
                message.obj = "我是消息";
                message.what = 1;
                mHandler.sendMessage(message);
            }
        });

这里总算看到我们第三个关键对象Message了,它内部封装了变量和方法供我们封装我们所要传递的消息,上面代码中的obj是一个Object类型的变量,所以可以通过它传递任意类型的消息。而what是一个int类型的变量,它主要用于当发送多条消息时,区别是从哪发送过来的,调用sendMessage()将我们封装好的Message发送出去后,最终Message会进入上面子线程创建Handler中handleMessage的参数(待会源码会分析最终如何传递到这个方法里),然后我们就可以对消息进行处理啦。整个过程差不多就是这样。上面创建消息对象使用了Handler中的obtainMessage()方法,而不是直接new创建的。其实是因为这个方法内部维护了一个消息池,当池不为空时,从池中取出Message复用返回,避免过多的创建Message对象占内存,这也是安卓官方提倡我们创建Message的方式。照着下面Handler.obtainMessage()方法链代码理解一下我上面所说的,如下

public final Message obtainMessage()
    {
        return Message.obtain(this);
    }
public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;

        return m;
    }
public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

所以我们以后要创建Message就最好直接使用Handler的obtainMessage()方法或者Message.obtain()方法哦。

接下来重点分析一下上面mHandler.sendMessage(message)方法内部做了什么吧,代码调用链如下

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
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);
    }
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

方法内部逻辑不算很多,总要记住三个参数即可,分别是MessageQueue,Message和uptimeMillis。首先看下MessageQueue,首次出现在上面第三段代码方法里面第一行,将mQueue赋值给了一个新的MessageQueue对象。我们在Handler里面找找哪个创建了这个mQueue对象,可以看到其实就是在我们刚开始创建的Handler时候构造函数就创建了一个mQueue,如下

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

代码16行通过mLooper的属性mQueue获取到mQueue的,我们在Looper这个类搜索下这个属性在什么时候被赋值的,发现是在Looper带一个参数的构造函数赋值的,代码如下

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

这里就是直接new创建的MessageQueue,而构造函数那个参数代表消息队列MessageQueue是否可以被停止,true表示可以停止消息队列(消息队列停止就是指将消息队列中未出列的消息全部清空,而我们刚刚上面分析了ActivityThread源码时发现Looper在休闲城是不能被停止的,其实Looper中的quit()方法内部就是调用MessageQueue中的quit()方法进行停止消息,如果当前线程是主线程,会抛异常,具体代码就不列出来,大伙自己搜索Looper和MessageQueue方法quit()吧)

这里Looper构造函数是私有的,说明Looper内部肯定哪里调用了这个构造创建了Looper对象,搜索一下发现如下代码

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

代码第9行就是创建了一个Looper放入了sThreadLocal中,绕了一大圈,结果是系统一开始就帮我们在主线程创建了mQueue对象。这里再做个小总结,系统启动,帮我们调用了Looper的prepare()方法,prepare()方法内部创建了Looper对象并放入sThreadLocal,保证每个线程只拥有一个唯一的Looper。Looper内部又创建了MessageQueue对象提供给Handler。当我们创建Handler时,Handler中就获取了绑定的Looper和Lopper中的MessageQueue对象。

说完了三个参数中的MessageQueue后,我们接着下一个参数Message,这个参数由我们调用Handler的sendMessage()方法时传入。其中比较有关注的是如下代码

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

代码中第2行让Message中的target指向了当前类对象,而target在Message中就是一个Handler对象。这里我们记Message中的target是跟我们创建的Handler对象绑定起来的,稍后会用到

最后一个参数是uptimeMillis,当我们不是发送延迟消息的时候,这个值是等于系统开机到发送消息时刻的毫秒数,所以我们先记住它是记录我们发送消息对应的一个时间,后面会用到

那消究竟发送到哪了呢?接下来我们来分析一下这个的源码路线,看到我们调用sendMessage()方法后,最终是走到如下的代码

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

最终是调用了MessageQueue中的enqueueMessage()方法将要发送的消息和时间传递过去,看到MessageQueue中的enqueueMessage()方法如下

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

首先看到上面第2行,就是判断Message中的target是否不等于null,通过我们刚刚上面的分析可知target指的是Handler。代码20行将一个全局的Message对象mMessages赋值给了p,我搜索了一下,这个mMessages只有在我们移除消息队列消息的时候才被赋值,我们此时并无移除消息,所以此时的p为null。因此会进入第22行的if判断,将我们传入的msg的next属性指向p,也就是指向null,然后再将mMessages指向当前的msg。这个next在Message中是Message对象,从这里我们可知MessageQueue存放Message并不是通过队列的形式存放,其内部其实是使用了单链表的形式,然后根据上面还判断了一下传入的时间跟之前可能存在的Message进行了比较可总结,MessageQueue存放Message是采用单链表形式,根据每个Message发送时的时间,逐个用Message中的next执行串联起来的。

到这里我们了解了Handler创建的注意点,发送消息最终如何入队到MessageQueue中了。那么最终消息是如何传递到我们Handler中的handleMessage()方法中的呢?这个问题显然就得转化为消息入队后是如何出队的。

出队的逻辑是在Looper中的loop()方法。还记得我们看ActivityThread源码时,最后抛异常前面就是调用了Looper的loop()方法吗?我当时还说这个方法内做了一个死循环,所以我们不会看到系统给我们抛异常。事不宜迟,我们赶紧找到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;

        // 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();

        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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

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

代码第2行又做了Looper不为null的处理,为null就会抛出我们刚刚一开始看到在子线程直接创建Handler的异常。之后获取到Looper创建的MessageQueue。紧接着看到代码第13行,就是做了一个for的死循环,这里也解释了我上面提及的loop()方法内做了一个死循环。那这个死循环内部又做了什么呢?我们看到代码第14行,是调用了MessageQueue中的next()方法,其实next()方法内部也是执行了一个死循环,获取当前系统时间到开机的毫秒值,然后不断从MessageQueue中取出在这个毫秒值之前的Message并返回,具体代码就不粘了,有兴趣的朋友可以自己看看。14行后还给了个注释说调用next()方法可能导致阻塞,因为MessageQueue的next()方法内部做了一个死循环,如果获取不到消息,会不断的循环等待消息到来,所以会阻塞导致下面的代码得不到执行(具体代码可自行查看)。然后loop()过多的代码我们就不分析了,我们直接看到代码第36行当我们有消息需要发送时,最终是调用了36行的msg.target.dispatchMessage()方法将我们发送的消息传递过去。这个target我们刚刚上面说是什么了?没错!就是指我们创建的Handler!(忘记的自己跺个脚,没细看!)

那自然而然肯定就是看到Handler中的dispatchMessage方法了(绕晕了吧?哈哈,别晕,已经接近尾声)

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

代码第2行首先判断了msg的callback是否为null,这里callback在Message中是一个Runnable类型的属性。它不为null必须是我们调用了Handler一些列以post打头的方法,传入一个Runnable时,最终才会走到我们指定的Runnable中的run()方法去执行,handleCallback(msg)方法如下

 private static void handleCallback(Message message) {
        message.callback.run();
    }

可以看到就是简单的传递到我们最终重写的run()方法执行我们的逻辑

而此时我们并没有调用到post打头的方法,所以逻辑走到了上面代码的第10行,看到我们重写的handleMessage(msg)方法了吧?!原来最终消息就是这样传递到我们重写的handleMessage(msg)方法中的!是不是突然柳暗花明?如果此时你再看看handleMessage(msg)这个方法,你会发现其没有内部实现,方法文档注释也写的很清楚,子类必须实现这个方法接收消息,不然最终消息就没人处理咯。这也解释了为什么我们创建Handler的时候一定得重写handleMessage(msg)这个方法了吧?是不是突然觉得原来一切是这样的?

/**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

最后做个总结:Handler异步消息处理机制就是我们创建Handler并重写handleMessage(msg)方法准备接收并处理消息,然后在需要的时候调用Handler的sendMessage(msg)发送消息,这个方法最终调用了MessageQueue方法enqueueMessage将消息根据时间以单链表的形式"入队",然后如果是在主线程创建的Handler,则系统会帮我们调用Looper.loop()方法进行死循环遍历取出MessageQueue中的message,取到就通过Message绑定的Handler对象target,调用到Handler中的dispatchMessage(msg),这个方法内部最终将消息又传入了handleMessage(msg)方法中,由于我们重写了这个方法,所以最终处理消息交给了我们。这就是整个Handler异步消息处理机制,不知看到这的小伙伴是否大概知道了Handler的源码如何分析了呢?然后盗用郭神的图片展示流程如下。


Handler是为子线程不能刷新UI而生的,所以很多其它在子线程刷新UI的方法内部都用到了Handler,分别有如下几个方法(参考郭神)

1. Handler的post()方法

2. View的post()方法

3. Activity的runOnUiThread()方法

这些方法其实底层代码实现就是用的Handler,这里就不再过多分析了,感兴趣的小伙伴可以看下源码(不会很多),自行检验自己是否理解Handler机制原理。

Handler之内存泄漏

Handler万一使用不当会导致内存泄漏,这里再多说一下如何解决这个内存泄漏的问题。其实导致Handler内存泄漏的根本原因就是我们创建的非静态内部类对外部类Activity持有隐式的强引用导致垃圾回收器无法回收Activity(这句话看不懂的童鞋建议先看下内存泄漏的几种常见案例吗,之后我也打算总结一下这个)解决这个问题其实有几种方案,下面说下做法

1.将handler声明为静态的,也就是加static关键字即可,因为静态内部类不会对外部类持有引用(推荐做法)

2.单独创建一个类继承Handler,实现handleMessage(msg)方法,这样也可以避免内存泄漏,但多了一个类,不推荐

3.在Activity的onDestroy()方法中执行如下代码(推荐)

 @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mHandler != null) {
            mHandler.removeCallbacksAndMessages(null);
            mHandler = null;
        }
    }

好了,关于Handler源码分析就到这里了,不知你是否掌握了呢?有问题或者发现博客问题欢迎提出指正。

最后,按照我个人习惯,给大家分享两个对程序猿有用的谷歌插件

1.Insight.io for Github

这插件用于在GitHub展示项目目录结构和搜索关键字,可设置快捷键等很多花式玩转GitHub的操作,可以翻墙滴小伙伴可点击下面链接跳转下载安装。不能翻墙的小伙伴我在最下面准备了两个crx文件,可以直接拖动到谷歌浏览器插件面板安装

Insight.io for Github

2.JSON-handle

平时我们在谷歌浏览器测试API接口时,看到返回的json串堆积在一起,而且字体字形非常难看,每当这时候你是不是很崩溃?那么这款插件你必须安装了,至于效果我就不展示了,绝对亮瞎你的眼,同样可以翻墙的童鞋点击下面网址跳转

JSON-handle

俩插件,密码:qxs8
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值