Android

Handler消息通信机制,原来如此简单

2018-11-16  本文已影响171人  Dora_Liang

前言


相信不管是工作还是面试,大家都会跟Handler打交道,而且Android许多地方,或多或少都会看见Handler的身影(AsyncTask,HandlerThread,IntentService等)。所以掌握Handler机制非常重要。该文主要是对Handler机制进行解析以及面试中经常的问题,对Handler的用法就不过多介绍。

一、主要概念简述

具体它们间的通信关系,如图: Handler通信流程图.png

二、源码解析

Handler源码分析

1.构造方法

虽然Handler的有多个构造方法,但是都离不开

public Handler(Callback callback, boolean async)

这个构造方法,例如:当我在主线程创建一个空参构造的Handler,里面其实默认传入callback为null,async为fasle。

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

我们跟着看下这个this到底做了什么?

public Handler(Callback callback, boolean async) {
        //这里判断Handler是否发生了内存泄漏
        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 that has not called Looper.prepare()");
        }
        //获取Looper里面的消息队列
        mQueue = mLooper.mQueue;
        mCallback = callback;
        //这个async是否异步发送消息
        mAsynchronous = async;
  }

2.Handler发送消息

//通过Handler发送消息,从这里开始
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) {
        //获取Looper的消息队列
        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) {
        //这个target代表的是发送该消息的Handler
        msg.target = this;
        //判断是否异步发送消息
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //消息入队,具体实现在MessageQueue
        return queue.enqueueMessage(msg, uptimeMillis);
 }

通过上述的源码分析,这样就完成了一次消息入队的过程。

//将Runnable添加到消息队列
public final boolean post(Runnable r) {
        //getPostMessage(r)返回一个Message
        return sendMessageDelayed(getPostMessage(r), 0);
}

//将Runnable赋值给Message的callback变量
private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
}

/**后面的发送消息就跟前面分析的一样**/

通过分析上面两种发送消息的方式,也将导致Handler处理消息也有两种方式。

3.Handler处理消息

/**
 *  处理系统消息
 */
public void dispatchMessage(Message msg) {
        //判断message里面callback是否为空
        if (msg.callback != null) {
            //处理post()发送的消息,内部其实就执行run()方法
            handleCallback(msg);
        } else {
          //处理sendMessage()发送的消息
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //重写该方法,进行消息处理。该方法是空方法,运行在主线程
            handleMessage(msg);
        }
}

Handle内部消息处理其实就做了两件事


Looper源码分析

在Looper里面会创建一个ThreadLocal静态变量,并将Looper作为参数(副本)传给它。将ThreadLocal作为Key,Looper作为Value,存在Thread的ThreadLocalMap。这样做起到了线程隔离,每条线程只能获取和存储本身独立的Looper。

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

1.Looper创建与获取

调用Looper.prepare( )就可以初始化当前线程的Looper。

/**
 * 初始化Looper,默认传true,允许退出Looper循环
 */
 public static void prepare() {
        prepare(true);
 }

//往下跟踪
private static void prepare(boolean quitAllowed) {
        //从ThreadLocalMap里面获取当前线程的Looper
        //如果不为空,抛出异常。(每条线程有且只有一个Looper)
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //不为空,创建一个Looper给存在ThreadLocalMap
        sThreadLocal.set(new Looper(quitAllowed));
}

这个是在子线程中,我们手动创建Looper。如果是在主线程中,使用prepareMainLooper( )创建。

 public static void prepareMainLooper() {
        //初始化Looper,不允许退出Looper循环
        prepare(false);
        //对Looper对象加同步锁
        synchronized (Looper.class) {
            //如果不为空,抛出异常
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            //获取Looper
            sMainLooper = myLooper();
        }
  }

我们看下Looper的构造方法,可以知道,当我们初始化一个Looper,内部都会帮我们创建好了一个消息队列MessageQueue

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

获取Looper:

/**
 * 获取存储在ThreadLocalMap的Looper
 */
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

关于ThreadLocal:在ThreadLocal内部会维护一个ThreadLocalMap静态内部类,用来获取与存储线程的本地副本。

2.Looper.loop( )消息循环

 public static void loop() {
        //获取Looper
        final Looper me = myLooper();
        //如果为空,抛出异常(需要先初始化Looper)
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //获取Looper里面的消息队列
        final MessageQueue queue = me.mQueue;

        //确认本地身份和跟踪身份令牌
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        //进入for死循环
        for (; ; ) {
            //获取下一个消息
            Message msg = queue.next(); // might block
            //如果为空,消息队列阻塞
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            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 {
                //分发消息给Handler(target就是该消息的发送者Handler)
                //在进行loop前,需要先创建Handler
                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);
            }
            //确保消息在分发的过程中,线程身份没有被破坏
            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();
        }
    }

整个Looper循环的过程分为:

3.Looper退出

分为两种分式:安全退出与非安全退出

/**
 *  直接退出
 **/ 
public void quit() {
        mQueue.quit(false);
}

/**
 *  安全退出(消息队列中的先退出延时消息,再处理完已有的消息后退出Looper)
 **/ 
public void quitSafely() {
        mQueue.quit(true);
}

注:

在主线中创建Handler,主线程会帮我们初始化Looper和进行looper循环,但是在子线程中创建Handler我们需要先Looper.prepare( ),在new Handler(Looper.myLooper()),最后进行Looper.loop( )进行循环。

public static void main(String[] args) {
        ...
        //初始化Looper
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        //获取主线程中的Handler
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        //Looper循环
        Looper.loop();

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

分析到这里,Handler的核心原理已经差不多,休息一下。

Message与MessageQueue源码分析

1.Message

Message主要用来存储各种信息的序列化对象,里面有几个常用的变量。

 //消息标识 
 public int what;
 public int arg1;
 public int arg2;
 public Object obj;
 //消息延时时间
 long when;
 Runnable callback;
 //发送消息的Handler
 Handler target;
 //下一个消息
 Message next;

2.MessageQueue

2.1、消息入队

在分析Handler发送消息过程中,我们会看到queue.enqueueMessage(msg, uptimeMillis)进行消息入队。

  /**
     * 消息入队
     * @param msg 消息
     * @param when 延时时间
     * @return
     */
    boolean enqueueMessage(Message msg, long when) {
        //如果target(Handler)为空,抛出异常
        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 {
                //否则插入消息队列中间,根据消息创建时间顺序插入
                //消息是队列中最早的异步消息
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                //进入for死循环
                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;
            }
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
2.2、消息出队

消息在Looper里循环中取出,消息怎么出队,我们看下源码。

 Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        //如果nextPollTimeoutMillis为-1,这时候消息队列处于等待状态。   
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            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) {
                  
                    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 = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        //消息标记为已使用
                        msg.markInUse();
                        //返回消息
                        return msg;
                    }
                } else {   
                    //如果没有消息,nextPollTimeoutMillis = -1,下次循环消息队列则处于等待状态
                    nextPollTimeoutMillis = -1;
                }
                //退出Looper循环
                if (mQuitting) {
                    dispose();
                    return null;
                }

              ...

            // 计数重置为0
            pendingIdleHandlerCount = 0;
            
            nextPollTimeoutMillis = 0;
        }
    }

消息队列中消息由Handler存入,由Looper取出。串行方式。


分析到这里,Handler的消息通信机制已经分析完了,回头看下原来如此简单。最后我们总结下

三、总结

四、参考文章

Android进阶——Android消息机制之Looper、Handler、MessageQueen

上一篇下一篇

猜你喜欢

热点阅读