Android:从源码剖析Hander机制

313 阅读23分钟

引言

Hander机制是安卓开发中一个重要的消息机制,也是面试的常客。本篇文章是结合书籍和源码,进行一个梳理总结,方便大家更好的理解Hander机制

Hander的两种用法及使用场景

这里介绍两种最为常见的用法以及使用场景

一丶使用HandersendMessage(Message msg)方法。

该方法对应延迟发送的方法sendMessageDelayed(Message msg,long delayMillis)

使用场景: 需要在子线程进行耗时操作,完成后,利用Hander通知UI进程也就是主线程刷新UI。 使用步骤:

步骤1. 在主线程中实例化Hander。这里介绍两种实例化方式。

       private static final int UPDATE_UI = 0; /*整形常量,标识要进行的动作,设置常量的作用在于将关注点放在操作本身而不是变量的值*/
        
        /*第一种实例化Hander的方法,重写handleMessage方法*/
        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case UPDATE_UI:
                        /*在这里进行更新ui*/
                        break;
                }
            }

        };
    
        /*第二种实例化Hander的方法,构造函数中传入Hander.Callback接口,该接口也只有一个同名handleMessage方法,不过的多了一个Boolean类型的返回值*/
        private Handler mHander = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                switch (msg.what) {
                    case UPDATE_UI:
                        /*在这里进行更新ui*/
                        break;
                }
                return true;
            }
        });

观察这两种实例化的方式,是不是感觉有些似曾相识?我们在创建新的Thread的时候,貌似也差不多,要么通过重写Threadrun()方法,要么就是在构造方法中传入一个Runnable接口,这里估计也是借鉴的这一种思想。

同时到这里产生一个问题: 第二种实例化方法中为啥会有返回值?返回值的作用是什么? 我们查看源码HanderdispatchMessage方法(大家先别管这个方法啥时候调用,只要知道消息经过辗转后最终会到这里进行分发就行了),源码如下:

    public void dispatchMessage(Message msg) {
             <!--先不管这个判断-->
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            <!--看下面这一行,mCallbck即为第二种实例方法中构造方法中传入的入Hander.Callback接口-->
            if (mCallback != null) { //不为空,则执行接口的handleMessage方法
                if (mCallback.handleMessage(msg)) { //如果返回值为true,直接返回
                    return;
                }
            }
                <!--执行hander本身的handleMessage方法-->
                    handleMessage(msg);
        }
    }

经过查看源码,我们得知,当使用第一种方法实例化hander时,mCallback(Hander.Callback)为空,最终会调用Hander自身的handleMessage(Message msg)方法当。而使用第二种方法实例化Hander时,会首先调用传入的Hander.Callback接口的handleMeassage(Message msg)方法,而该方法如果返回值为true表示消息只由该接口处理,而如果返回false,则由该接口handleMessage(Message msg)方法处理完成后会紧接着由Hander本身的handleMessage(Message msg)方法处理。

步骤2. 在子线程中进行耗时操作,完成后利用HandersendMessage()方法通知主线程.

        new Thread(){
            @Override
            public void run() {
                /*模拟耗时操作*/
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                 /*耗时操作完成,利用hander发送消息给主线程*//*
                // Message msg = new Message();  获取消息的第一种方法,直接实例化Message
                // Message msg = Message.obtain(); 获取的第二种方法,从消息池中获取消息,推荐使用
                Message msg = mHander.obtainMessage(); //获取消息的第三种方法,本质上就是第二种方法
                msg.what = UPDATE_UI;  //设置消息的参数,便于区分,如果一个参数不足以区分,Message类还提供arg1和arg2两个整形参数,以及object参数用于设置
                mHander.sendMessage(msg); // hander发送消息的方法
            }
        }.start();

在子线程中耗时操作完成后,需要生成一个消息由主线程实例化后的Hander发出去。生成消息有三种方式,代码中也注释了,如果需要频繁的发送消息,推荐使用第二种方式和第三种方式,第三种内部也是调用的Messageobtain()方法.,该方法从Message自身维护的消息池中获取消息,比起直接实例,消息池能对消息对象进行回收利用,性能上更佳。消息发送完成后,经过一系列的辗转,主线程HanderhandleMessage()方法便会回调,我们便能根据相关的参数判断消息的类型,从而进行相应的操作处理。 以上便是第一种用法。下面介绍第二种用法和使用场景。

二. 使用Handerpost(Runnable r)方法。

对应的延迟发送方法,postDelayed(Runnable r,long delayMillis)

第一种使用方法是发送一个Message,而这种方法是发送一个Runnable接口出去,看上去差别挺大,实际上两者本质相同。其实,在第二种使用场景中,Hander内部会自动帮我们将Runnable接口封装在一个Message里面。 源码告诉了我们一切:

//发送Runnable接口时,hander内部的getPostMessage方法会将我们的runnable接口赋值为Message的callback属性
        private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            m.callback = r;
            return m;
        }

可以看到,Message内部有一个callback属性,而这个callback,类型便是我们之前postRunnable接口. 那么问题来了?这种带Runnable接口的消息Hander是怎么处理的呢?贴上一个熟悉的方法,也就是场景一中所贴过的,消息辗转之后,最终会调用的Hander中的dispatchMessage(Message msg)方法:

    public void dispatchMessage(Message msg) {
             <!--先不管这个判断--> (之前的注释)
             <!--之前让你们先不用管的判断,实际上就是用于处理携带runnable消息的判断,callback实际上就是post方法中的runnable参数-->
            if (msg.callback != null) {
                handleCallback(msg); //对于携带runnable的消息,hander直接调用该方法
            } else {
            <!--看下面这一行,mCallbck即为第二种实例方法中构造方法中传入的Callback-->
                if (mCallback != null) { 
                    if (mCallback.handleMessage(msg)) { //如果返回值为true,直接返回
                        return;
                    }
                }
                <!--执行hander本身的handleMessage方法-->
                handleMessage(msg);
            }
        }

可以看到,对于携带Runnable接口的消息,Hander直接调用自身的handleCallback(Message message)对其进行处理. 那么handleCallback(Message message)是怎样处理消息的呢?还是看源码:

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

可以看到,只是将Runnable接口中的run()方法执行了一遍,仅此而已,虽然是Runnable接口,但和线程没有任何关系。

隐藏的三兄弟 Looper 丶MessageQueue丶ThreadLocal

在上面的文章中,我们只接触到了 HanderMessage,似乎有了这两兄弟,Hander便可正常运转。在上面的文章,我们还说过,无论是发送有一个Message和发送一个Runnable接口,最终都是Message的方式发送,然后经过一系列辗转之后,会走到HanderdispatchMessage(Message msg)中进行分发,处理。那么消息辗转的过程是怎么样的呢?还是接着在源码中找寻答案:我们查看HandlersendMessage()方法:

 public final boolean sendMessage(Message msg)
        return sendMessageDelayed(msg, 0);
    }

可以看到,该方法在内部又调用了一个sendMessageDelayed方法,根据函数我们可以知道,普通消息实际上就是一个延迟时间为0的延迟消息,继续往下看:

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

可以看到,在该方法中对延迟时间进行了一下判断之后,又进入下一个sendMessageAtTime方法。虽然由一个sendMessage()方法开始,进行了很多次的Hander内部其它函数调用,但是我们不要觉得混乱,这样做其实无非就是对消息的发送时间进行各种判断,计算而已。继续跟进:

 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.小弟不才,这里先给上书上的解释。

MessageQueue的中文翻译是消息队列,顾名思义,它的内部存储了一组消息,以队列的形式对外提供插入和删除的工作。虽然叫消息队列,但是它的内部存储结构并不是真的队列,而是采用单链表的数据结构来存储消息列表。

看了解释,我们对MessageQueue有了大概的认识,就是一个单链表,用来存储发送出来的Message.我们看到,在上面的方法中,直接获得了成员变量mQueue的引用。那么对于Hander来说,我们并没有手动给它的mQueue赋值,那么mQueue是在哪里赋值的呢?很大概率是构造函数了,那我们去Hander的构造函数中瞅瞅。追寻源码我们发现,无论哪种方式实例化hander,最终都会到这个构造函数中进行最终的初始化。

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

我们在这里看到了mQueQue的赋值,不过,很奇怪,mQueue的引用来自于自身另一个成员变量mLooper的成员变量.而mLooper的值,是由 Looper.myLooper()这行代码赋值。这里,我们又见到另一位隐藏的兄弟,Looper.老规矩,先按照书上的话大概介绍下这位兄弟.

由于MessageQueue只是一个消息的存储单元,他不能去处理消息,而Looper就填补了这个功能。Looper会以无限循环的方式去查找是否有新的消息,如果有的话就处理消息,否者就一直等待着。 好了介绍完毕之后,我们进入Looper.myLooper()方法中,一探究竟。

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

我们看到,myLooper()方法只是将Looper中的sThreadLocal的值返回。我们先来看看这个变量长啥样。

  static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

看到这里,我们看到了第三个兄弟ThreadLocal,对于没接触过ThreadLocal这个类的人来说可能会一脸懵逼。先按照书上的话解释下:

ThreadLocal是一个线程内部的数据存储类,通过他可以在指定的线程中存储数据,数据存储后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。

对于没接触过的人是不是看了解释还是一脸懵逼。。。先来个demo便于理解。

public class MainActivity extends AppCompatActivity {

    static ThreadLocal<String> stringThreadLocal = new ThreadLocal<String>();
    static ThreadLocal<Boolean> booleanThreadLocal = new ThreadLocal<Boolean>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        stringThreadLocal.set("Lesincs");
        booleanThreadLocal.set(true);
        Log.d("stringThreadLocal的值为", stringThreadLocal.get());
        Log.d("booleanThreadLocal的值为", booleanThreadLocal.get().toString());
        new Thread() {
            @Override
            public void run() {
                stringThreadLocal.set("scniseL");
                booleanThreadLocal.set(false);
                Log.d("stringThreadLocal的值为", stringThreadLocal.get());
                Log.d("booleanThreadLocal的值为", booleanThreadLocal.get().toString());
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                stringThreadLocal.set("juejin");
                booleanThreadLocal.set(true);
                Log.d("stringThreadLocal的值为", stringThreadLocal.get());
                Log.d("booleanThreadLocal的值为", booleanThreadLocal.get().toString());
            }
        }.start();
    }
}

运行打印结果为这样:

03-07 04:49:23.425 2096-2096/? D/stringThreadLocal的值为: Lesincs
03-07 04:49:23.425 2096-2096/? D/booleanThreadLocal的值为: true

03-07 04:49:23.425 2096-2112/? D/stringThreadLocal的值为: juejin
03-07 04:49:23.425 2096-2112/? D/booleanThreadLocal的值为: true

03-07 04:49:23.429 2096-2111/? D/stringThreadLocal的值为: scniseL
03-07 04:49:23.429 2096-2111/? D/booleanThreadLocal的值为: false

看了demo和运行的结果我们大概可以感受到ThreadLocal的用法:

  • 实例的时候通过泛型指定值的类型
  • 通过set()方法进行赋值
  • 通过get()方法获取值

特点:

  • 在不同线程中,ThreadLocal变量的值相互独立。

那么为什么ThreadLocal有这么“神奇”的特点呢?原理是什么呢? 大家去Android Studio对着Thread类按着contrl点击一下,进入Thread源码,会发现Thread类中有一个变量长这样:

    ThreadLocal.ThreadLocalMap threadLocals = null;

可以看到这个变量是ThreadLocal的一个内部类ThreadLocalMap,我们把它当成一个特殊的map,它的keyThreadLocal,而values则是对应的值。 我们查看ThreadLocalget()方法:

 public T get() {
        Thread t = Thread.currentThread(); //得到当前线程
        ThreadLocalMap map = getMap(t);  //得到当前线程上面所说的ThreadLocalMap对象
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this); //this传入当前threadLocal对象,以该对象为键索引得到值
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue(); //如果未被赋值 返回默认值
    }

可以看到,对于ThreadLocalget()方法,每一次取值,都会先得到当前线程的ThreadLocalMap,再传入自身的引用,作为key,得到相应的value.对于不同线程,有不同的ThreadLocalMap变量,所以即使key(ThreadLocal变量)相同,value还是可以得到独立。 set()方法同理,还是贴一下源码。

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

set()方法也类似,不同的在于多了一个createMap方法,这里不禁感受到源码设计的巧妙。当整个项目中没有ThreadLocal的对象时,线程中的ThreadLocalMap并没有创建,而是当第一个ThreadLoal实例化时,才开始创建,避免资源白白浪费。

到这里你应该对ThreadLocal有了一定的理解。一言以蔽之:一个ThreadLocal变量的值之所以能在不同线程中相互独立,是因为每个线程中维护了一个ThreadLocalMap,在调用ThreadLocalget()set()方法时,会先得到当前线程ThreadLocal变量,然后在把ThreadLocal变量本身当成key从中赋值或者取值。

ThreadLocal大致了解后,我们继续回到Hander机制的正题。 上面说到 Looper类中有一个静态 ThreadLocal变量,泛型指定为Looper,即这个ThreadLocal变量的值为Looper

  static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

Hander的构造函数中初始化得到mLooper便是Looper的静态myLooper()方法.

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

可以看到,myLooper()方法中仅仅只是调用了sThreadLocalget()方法得到了Looper的值,那么看过上面ThreadLocal的demo之后,我们知道,ThreadLocal的值必须先进行set()方法,在调用get()方法才能返回set()方法的值,不然,只会返回默认的值,对于Looper,默认值肯定是null。那么主线程的Looper的值是在哪里设置的呢?对于本文,也就不卖关子了。对于我们Activity运行的Thread,也就是ActivityThread。系统已将帮我们进行了Looper的创建,查看源码:

public final class ActivityThread {
   public static void main(String[] args) {
        ....(省略无关代码)
        Looper.prepareMainLooper();  // 在这儿调用 Looper.prepareMainLooper, 为应用的主线程创建Looper
        ActivityThread thread = new ActivityThread();
        sMainThreadHandler = thread.getHandler();
       }
        Looper.loop();  //进行消息循环读取
        ....
    }
}

我们看到,调用LooperprepareMainLooper()后,后面又调用了Looperloop()方法。对于loop()方法,我们先不管。先看下LooperprepareMainLooper()方法做了些什么.

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

prepareMainLooper()中首先是调用了 prepare(boolean quitAllowed)方法,紧接着一个判断之后是这么一行代码:

                      sMainLooper = myLooper();

我们知道myLooper()方法是返回当前线程Looper的引用,可以看到在prepare(boolean quitAllowed)方法之后,又用sMainLooper保存了当前线程Looper的引用,那我们可以猜测,Looper的赋值肯定是在prepare(boolean quitAllowed)中了,我们看这个方法做了些什么呢?

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

果然,prepare(boolean quitAllowed)方法的作用就是实例化Looper并放入sThreadlLocal变量中。

可以看到,prepareMainLooper()方法先是调用了prepare(boolean quitAllowed)方法,然后将当前线程的Looper实例存入了sMainLooper这个静态变量中。这样,无论在哪个线程中,都能轻易的拿到主线程的Looper引用,笔者目前还不知道有啥用,但是想到Hander有两个构造函数重载是可以传入Looper对象的,猜测可能有其它高级用法吧。

继续,我们知道MessageQuequeLooper对象中的一个变量,那么我们看看Looper的构造方法中发生了些什么。

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

我们看到了在Looper构造函数中,实例了MessageQueue对象,将他赋值给自身的成员变量mQueue,以及将当前线程的引用保存。

在上面的ActivityThread中,我们还看到了Looper.loop()方法的调用,其实很好理解.我们得知在prepare(boolean quitAllowed)方法中,仅仅只是进行了MessageQueue的创建,此时,Message发送之后,已经可以加入到MessageQueue中,但是我们还需要一个操作对Message进行读取以及处理.看源码loop()方法:

    public static void loop() {
        final Looper me = myLooper(); //得到当前线程的Looper对象
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue; //得到MessageQueue对象

        // 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(); // MessageQueue中没有消息时,该方法阻塞.
            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();
        }
    }

我们可以看到loop()中有一个无限for循环,在循环体中,调用自身的MessageQueue对象的next()方法取出消息进行处理。我们看一看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();
            }
    //native方法,用以阻塞。当nextPollTimeoutMillis时长过去或者有新的消息来临,该函数才会返回,继而进行下面的操作。
            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; //用preMsg保存头结点引用
                        msg = msg.next;// 用msg保存第二个节点的引用
                    } while (msg != null && !msg.isAsynchronous()); //当存在第二个消息,并且是同步消息才打断循环?对这里有点暂时还有点疑问
                }
                if (msg != null) { 
                    if (now < msg.when) {
                    //若当msg的when属性大于当前时间,说明msg未到处理时间
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                    //设置nextPollTimeoutMillis的值为msg.when和now的差值,循环之后上面的native阻塞方法会阻塞相应的时间再处理消息
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                    //若当msg的when属性小于当前时间,说明msg已经可以处理
                        // 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 {
                    // 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) {
                    // 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()方法会一直阻塞在这里。当有新消息到来时,next()方法会返回并将其从单链表移除。有移除,就必有插入,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; //将参数的when赋值给msg的when属性,这里不深谙其细节,我们可以推测其作用是表示msg执行的时间
            Message p = mMessages;
            boolean needWake;
    // p为链表的头结点,p==null表示消息队列为空,,从判断我们可以得知when属性越小,则排在越前面,越早被处理.
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
    //满足消息队列为空或者when=0,或者when属性小于当前头结点的when属性时,将新的message插入链表头部。
                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;
    //如果对于一个新来的消息,其when属性位于居中的位置怎么处理呢?对于链表,肯定是查找到一个合适的位置插入.
                for (;;) { //无限循环
                    prev = p; //利用prev保存左边的节点
                    p = p.next; //利用p保存右边的节点
                    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;
    }

对于MessageQueueenqueueMessage(Message msg, long when)方法,看过注释之后你会大概了解它的原理。对于每个Message,有发送时机的先后,也有指定发送延迟时间的不同。系统于是统一在内部指定了一个when属性,用来表示消息处理的时间,when的值越大,表示越晚发送。这个属性在Message进入MessageQueue之前已经被计算好了.在进入MessageQueue时,MessageQueue调用enqueueMessage(Message msg, long when)进行消息插入,并且插入的规律是按照when属性的大小顺序,越大越靠近尾部。

好了,我们从HandersendMessageAtTime(Message msg, long uptimeMillis)方法扯了这么多,但是如果上面所说的大家都理解了的话,下面的就很容易理解了。我们回到刚才说到HandersendMessageAtTime(Message msg, long uptimeMillis)方法:

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

我们看到拿到消息队列之后,Hander调用了自身的enqueueMessage方法.Hander也有enqueueMessage方法?excuse me?继续看源码

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

果然,自身的enqueueMessage方法内部还是调用的MessageQueueenqueueMessage方法,将消息插入消息队列中。至此,已经没Hander啥事了,消息已经到了MessageQueue中,并由Looper的无线循环方法结合MessageQueuenext()方法进行检索。检索到消息之后看代码:

...
 msg.target.dispatchMessage(msg);
...

我们看到了熟悉的dispatchMessage()方法,也就是上文说的消息经过一系列辗转之后最终会调用的方法,那么msg.target的值肯定就是我们发送它的hander啦。target是在哪里赋值的呢?还记得HanderenqueueMessage方法吗?

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//设置消息的target属性为hander自己。谁把你发出来的,最终谁处理。
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

可以看到,msg.target的值就是在这里赋予的。

因为msg.target.dispatchMessage方法是在Looper循环方法中调用的,那么最后的handleMessage也是在Looper所在线程,也就是主线程中调用,我们成功的在主线程中处理了消息!

总体梳理

源码终于讲完了,下面还是梳理一下整个过程吧。

准备工作:

  1. 首先是Looper调用prepare()方法(主线程中调用prepareMainLooper方法),实例化Looper对象,并将其值放在threadLocal类里面,保证各个线程的Looper对象互不影响。
  2. Looper在实例化的时候,实例化MessageQueue对象并保存到自身成员变量mQueue.
  3. Looper调用其Loop()方法在主线程中进行循环调用mQueuenext()方法读取mQueue中的消息。若mQueue该队列中没有新的消息,该方法会阻塞。若读到消息,消息从队列中移除,并且调用Messagetarget属性(即发送它的hander对象)的dispatchMessage()方法进行消息处理.

Hander发送消息:

  1. Hander在主线程中实例化,在实例化的同时,保存当前线程的Looper引用以及LooperMessageQueue的引用mQueue

  2. Hander调用自身sendMessage方法。最终会调用自身的sendMessageAtTime方法,该方法中,又会调用自身enqueueMessage的方法,在enqueueMessage中,将自身的引用赋值给messagetarget属性,然后调用mQUeueenqueueMessage方法,将消息插入消息队列。

  3. 消息插入队列后,请看准备工作的第三步。

  4. dispctchMessage方法中会判断msg类型,是普通消息还是携带Runnable接口的消息。如果是携带Runnable接口的消息,直接调用runnable接口run()方法。如果是普通消息。调用handleMessage方法处理之。

相关问题

  1. Looperlooper()是一个无线循环方法,为什么不会造成ANR?

    关于这个问题,想了想是挺奇怪的,于是去网上搜索下,感觉下面这个文章的说法还挺让人信服的,有的太多于深层次,有的又太浅显,大家暂时按着下文中这样理解吧。

    文/HongJay(简书作者) 原文链接:http://www.jianshu.com/p/cfe50b8b0a41

  2. Hander会导致内存泄漏吗?如果会的话,怎么解决?

    关于这个问题,前几天看过掘友的一篇总结的很好。也贴上来吧,不要重复造轮子哈哈。

    文/Carson_Ho(简书作者) 原文链接:http://www.jianshu.com/p/cfe50b8b0a41

参考

书籍:《安卓开发艺术探索》

最后

这是在掘金上第二次写文章,记得写第一篇文章时,顺风顺水,洋洋洒洒几小时就捣鼓好了,还也得到了不少掘友的鼓励和喜欢,很是欣慰。而这次写得感觉有点精神分裂,文中有很多不足的地方,比如MessageQueuenext()方法,自己也理解得有点晕乎,还请见谅。因为这次的内容相比上次的要复杂得多,而且还是结合着源码在分析,看源码是件繁琐的事情,但是既然你要写文章,要对你的读者负责,你就不敢马马虎虎,这也是我想写文章的一部分原因吧,能督促自己多看看源码。建议大家没事也可以多多看看源码,对于源码中比较复杂的东西,有的可以选择性忽略,关注其中最重要的东西,让自己能够消化。祝大家心明眼亮!