存储架构

Android Handler 源码探索

微信扫一扫,分享到朋友圈

Android Handler 源码探索
0

Handler 作为一种异步消息通信机制,通常在面试的时候会被问到源码部分,本篇文章就通过源码来揭开Handler的神秘面纱,真正了解Handler的本质。

一提起Handler,相信大家都会想到几个重要的类:Handler、Message、MessageQueue、Looper。那这四者是如何协作的呢?下面我们一一来分析这几个类的源码。

二、源码解析

首先先放一张整体流程图,如下:


整体的流程是: Message通过obtain()方法获得一个message,并对message进行属性及所需传递数据的设置,通过Handler的sendMessage()方法发送消息,MessageQueue通过enqueueMessage()方法将消息添加入消息池中,并通过Looper.loop()方法进行消息的运输,最后MessageQueue通过next()方法将消息从消息池中取出,并通过message的target属性找到对应的Handler,由对应的Handler调用dispatchMessage()方法进行消息的分发处理。

Message为传输的内容,所以我们从Message开始探索。

Message

Message 的属性主要如下:

/**用来标志一个消息*/
 public int what;
/**如果传递的数据是整型的,可直接使用这两个参数*/
 public int arg1;
 public int arg2;
 
 /*package*/ int flags;
 /*package*/ long when;
 /**如果传递的数据是复杂的类型,可放在Bundle中传递*/
 /*package*/ Bundle data;
 
/** 发送和处理消息所关联的Handler*/
/*package*/ Handler target;

/** 消息池*/
public static final Object sPoolSync = new Object();
private static Message sPool; //消息链表的链头
private static int sPoolSize = 0;
	
/**消息池所放最多消息数目*/
private static final int MAX_POOL_SIZE = 50;

复制代码

从以上源码我们可以看出Message是有一个存放消息的消息池的,那这个消息池具体有什么作用呢?我们从Message的构造函数继续来看。

众所周知,Message获取一个对象的方式有三种:

  1. new Message()
  2. Message.obtain()
  3. Handler.obtainMessage()

下面为Handler.obtainMessage()的源码:

public final Message obtainMessage()
    {
        return Message.obtain(this);
    }
复制代码

从上面代码我们可以看到,Handler.obtainMessage()事实上还是调用的Message的obtain()方法。那么我们继续来看Message的obtain()方法:

/**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    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.obatain()是先从消息池中去取消息,如果消息池中有则将该消息重置属性参数,然后返回;如果消息池中没有消息则创建一个新的消息并返回。这样做就可以避免重复过多的创建Message对象,减少了内存的占用。

在上面的代码中我们看到了从消息池中取出消息重用的部分,那消息是如何被添加到消息池中的呢?是通过Message的recycleUnchecked()方法:

void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // 将该消息打上被回收的标签,并清空该消息的参数部分,加入到消息池中。
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool; //next 为链头指向的下一个消息,sPool为链头
                sPool = this;
                sPoolSize++;
            }
        }
    }
复制代码

从上面的代码我们可以看出,消息池的本质其实是一个单链表,当有消息进入消息池的时候就将该消息插入到链头。从消息池中取消息的时候也是从链头开始取出。 那么recycleUnchecked()方法何时被调用呢?在MessageQueue和Looper中都有该方法的调用。 MessageQueue中的removeMessage()方法:

void removeMessages(Handler h, int what, Object object) {
        if (h == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

            // Remove all messages at front.
            while (p != null && p.target == h && p.what == what
                   && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();  //调用
                p = n;
            }

            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && n.what == what
                        && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked(); //调用
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }
复制代码

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 (;;) {
            //...
            msg.recycleUnchecked(); //调用
        }
    }

复制代码

MessageQueue

MessageQueue为管理Message的单链表,虽然MessageQueue的名字是消息队列,但不能被它的名字所误导,其本质是披着队列名字的单列表。Handler为MessageQueue添加消息,然后由Looper从中取出消息,从而实现消息的运输传递。

由于MessageQueue与Looper是一对一的关系,Looper的唯一性保证了MessageQueue的唯一性,所以其初始化是在Looper中完成的,一般由Looper.myQueue()获得一个消息队列。

//quitAllowed 为是否允许中途退出的标志,调用Native方法,暂不做深入探究
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
复制代码

添加消息主要为enqueueMessage()方法:

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) { //message必须有target,即message由handler发送而来
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {  //判断该message是否已被使用
            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) {
                // 如果该队列为空,或者新加入的消息所用时间最短,则将该消息作为链表表头,如果该队列是在阻塞状态则唤醒该队列
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                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;
    }
复制代码

消息出队的方法为next()方法,下面让我们看下具体源码实现。

Message next() {
        //当应用退出重启的时候,消息loop会退出或者被释放,这个时候不支持message被取出队列,所以直接结束该方法。
        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) {
                // 检索下一个消息,并返回
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;  //mMessages始终指向消息队列的头节点消息,如果该对象为空,则说明消息队列为空
                if (msg != null && msg.target == null) {
                    //如果消息没有target,那它就是一个屏障,需要一直往后遍历找到第一个异步的消息
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                         //如果下一个消息还没有准备好,则设置一个定时器当该消息准备好时唤醒它执行
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 取出消息
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                        //保证mMessages始终指向头节点
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    //没有消息.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
                //·····
        }
    }

复制代码

Looper

在了解Message和MessageQueue后,我们已经知道Message是如何加入到MessageQueue中,那么在MessageQueue中,消息又是如何被调度的呢?下面就让我们一起来看下Looper的功能。

Looper的主要方法有prepare()和loop(),下面让我们依次来探索。

private static final String TAG = "Looper";
    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // 主线程中的Lopper

    final MessageQueue mQueue;  //当前Looper管理的消息队列 
    final Thread mThread;   // 当前Looper对应的线程

    private Printer mLogging;
    private long mTraceTag;
复制代码

Looper的属性比较简单,Looper主要通过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));
    }
复制代码

通过上面的源码我们可以知道,每一个Looper都是通过一个ThreadLocal对象来存储。而且在存储前对该ThreadLocal.get()方法进行了判空,说明Looper.prepare()方法只能被调用一次,该ThreadLocal对象只能存储一个Looper,从而保证每个线程只能有一个Looper.那么ThreadLocal是怎样管理线程和Looper的呢,让我们看下它的主要源码:

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }
    
     public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
复制代码

通过上面代码我们可以看到,ThreadLocal当中有一个ThreadLocalMap来存储Thread和Looper数据,并关联二者。好了,现在我们知道一个线程只有一个唯一的Looper后,让我们继续看下Looper的构造函数。

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

复制代码

我们知道Looper的prepare()方法只能调用一次,那么由此而知Looper的构造函数也只会调用一次。而MessageQueue是在Looper的构造函数中创建的,那么由此可以推断Looper的唯一决定了MessageQueue的唯一,也就是说一个线程有唯一的一个Looper,这个Looper管理一个唯一的MeesageQueue.

现在理清了Looper和MessageQueue的关系,让我们看下Looper是如何管理MessageQueue的。

public static void loop() {
        //获得当前线程的Looper
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //获得当前Looper管理的消息队列
        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();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        boolean slowDeliveryDetected = false;
        //通过无限循环取出消息进行调度
        for (;;) {
             //如果没有阻塞则从消息队列中取出消息
            Message msg = queue.next(); // might block
            if (msg == null) {
                // 如果没有消息说明该消息队列已经退出
                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 traceTag = me.mTraceTag;
            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
            if (thresholdOverride > 0) {
                slowDispatchThresholdMs = thresholdOverride;
                slowDeliveryThresholdMs = thresholdOverride;
            }
            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
            final boolean needEndTime = logSlowDispatch;

            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            try {
                //通过Message属性找到对应的Handler 分发消息
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (logSlowDelivery) {
                if (slowDeliveryDetected) {
                    if ((dispatchStart - msg.when) <= 10) {
                        Slog.w(TAG, "Drained");
                        slowDeliveryDetected = false;
                    }
                } else {
                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                            msg)) {
                        // Once we write a slow delivery log, suppress until the queue drains.
                        slowDeliveryDetected = true;
                    }
                }
            }
            if (logSlowDispatch) {
                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
            }

            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);
            }
            //进行Message的回收,释放占据的资源
            msg.recycleUnchecked();
        }
    }
复制代码

由以上的源码分析,我们可以总结下Looper的主要功能:

1、绑定当前线程,保证当前线程唯一的Looper,同时保证唯一的MessageQueue;

2、执行loop()方法,不断从消息队列中调度消息,并通过消息的target属性找到对应的Handler 执行dispatchMessage()方法处理消息;

现在有了存储消息的工具和调度消息的工具,只缺少处理消息的工具了,这个工具正是大名鼎鼎的Handler。下面让我们一起去看看Handler的源码。

Handler

Handler的主要功能是什么呢?首先,Handler在子线程中将Message发送到MessageQueue中;然后,等待Looper调度消息后,通过Message的target属性找到发送该消息的Handler;最后,Handler在自己的dispatchMessage()中调用handleMessage()方法进行消息的最终处理。下面让我们一步步揭开源码,首先从Handler的构造函数开始:

public Handler(boolean async) {
       this(null, async);
   }

 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());
           }
       }
       //获得当前线程保存的Looper实例
       mLooper = Looper.myLooper();
       if (mLooper == null) {
           throw new RuntimeException(
               "Can't create handler inside thread " + Thread.currentThread()
                       + " that has not called Looper.prepare()");
       }
       //通过获得的Looper实例获取对应的MessageQueue,从而Handler和MessageQueue关联上了
       mQueue = mLooper.mQueue;
       mCallback = callback;
       mAsynchronous = async;
   }

复制代码

Handler主要的功能是进行消息的发送和处理。发送消息的方法主要为sendMessage()和post()方法。处理消息的方法主要为dispatchMessage()方法。

Handler发送的主要有两种类型:Message和Runnable。Message主要是通过sendMessage()的方式,Runnable主要是通过post()的方式。

public final boolean post(Runnable r)
   {
      return  sendMessageDelayed(getPostMessage(r), 0);
   }
复制代码

通过源码我们可以知道,post()的方法最终采用的还是sendMessage()的方式。

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;  //标记msg对应的handler
       if (mAsynchronous) {
           msg.setAsynchronous(true);
       }
       return queue.enqueueMessage(msg, uptimeMillis);
   }
复制代码

根据以上源码分析,我们可以看到sendMessage()方法最终是调用了MessageQueue的enqueueMessage()方法,实现了Handler将消息发送并加入到消息队列中。enqueueMessage()方法在前面已经讲过了,这里不再赘述。到现在已经比较清楚的知道,当消息加入到消息队列后,Looper会调用prepare()和loop()方法,在当前执行的线程中保存一个Looper实例,这个实例会保存一个MessageQueue对象,然后当前线程进入一个无限循环中去,不断从MessageQueue中读取Handler发来的消息。然后再回调创建这个消息的handler中的dispathMessage方法进行消息的处理。

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {  //如果是采用post()方式时走此方法
            handleCallback(msg);
        } else {
            if (mCallback != null) {  //采用Handler.Callback 为参数构造 Handler时走此方法 
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    
       private static void handleCallback(Message message) {
        message.callback.run();
    }
    
    //空实现
      public void handleMessage(Message msg) {
    }
复制代码

最后我们可以看到一个空实现的handlerMessage()方法,这是为什么呢?因为消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后根据msg.what进行消息处理,例如:

private Handler mHandler = new Handler()
	{
		public void handleMessage(android.os.Message msg)
		{
			switch (msg.what)
			{
			case value:
				
				break;
 
			default:
				break;
			}
		};
	};
复制代码

好了,以上就是全部的源码探索啦,最后再总结一下:

1、Looper通过prepare()方法保存一个本线程的实例,然后这个Looper实例保存一个MessageQueue,因为prepare()方法只能调用一次,所以保证了每个线程只对应唯一一个Looper实例,从而保证了一个Looper对应唯一的MessageQueue。

2、当前线程会在Looper的带领下进入一个无线循环从MessageQueue中去不断取消息,然后通过Message的target属性找到对应的Handler,然后通过dispatchMessage()方法来进行消息的分发。

3、Handler在构造的时候首先会找到当前线程的Looper实例,并通过该实例找多该Looper对应的唯一的MessageQueue,由此Handler和MessageQueue之间建立联系。Handler在sendMessage()的时候,会为每个消息的target赋值自身,然后再加入到MessageQueue中。

4、在我们构造Handler时会同时复写handleMessage()方法,所以在Looper找到Message对应的Handler后,由Handler调用dispatchMessage(),即最终回调handleMessage()进行最终消息的处理。

最后本次Handler源码分析结束~

阅读原文...

微信扫一扫,分享到朋友圈

Android Handler 源码探索
0
稀土掘金

基于多重替换方式的iOS代码混淆方案

上一篇

Swift3、4中的@objc、@objcMembers和dynamic

下一篇

评论已经被关闭。

插入图片

热门分类

往期推荐

Android Handler 源码探索

长按储存图像,分享给朋友