美文网首页程序员IT技术篇
Android SDK源码解析篇之Handler如何实现发送和处

Android SDK源码解析篇之Handler如何实现发送和处

作者: 非典型程序猿 | 来源:发表于2020-02-26 19:05 被阅读0次

    前言

    在Android的日常使用中,Handler的使用大家应该都不陌生,今天这篇文章会从Android SDK源码的角度分析Handler的实现原理。

    Handler的使用

     public static Handler mHander = new Handler(){
            @Override
            public void handleMessage(Message message){
                switch (message.what){
                    case 0:
                        break;
                    case 1:
                        break;
                        default:
                            break;
                }
            }
        };
    

    在主线程使用时,我们只需要在Activity或Handler中像上面一样创建一个新的Handler实例类,在需要发送消息的地方调用sendMessage()方法就可以进行Handler的使用了,这里发送消息的方法不唯一,还有其他的发送方法。

    Handler的基本原理说明

    学习过Handler的原理的同学都知道,Handler包括四个部分,Handler,Looper,MessageQueue,Message组成,当Messge到达以后会添加进MessageQueue消息队列,Looper负责循环遍历消息队列,当有消息时从队列中取出并用Handler进行回调。

    Handler源码解析

    首先我们打开Handler的源码.
    (如果你的应用的开发SDK版本和你的Android Studio所下载的SDK版本不匹配的话是无法看到详细的代码的,只能看到.class文件,解决这个问题只需要在SDK Manager中下载对应的SDK,重启一下Android Studio即可,ps:必须重启AS)

    处理消息的核心原理

    我们知道处理消息都是复写handleMessage方法,所以我们先去源码里查看这个方法

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

    注释也写的很清楚,那就子类必须实现这个方法来接收消息。
    那么我们继续看源码,看接收到消息时是如何执行到这个方法的,因此我们通过搜索查到了dispatchMessage()这个方法,所以我们继续看

        /**
         * Handle system messages here.
         */
        public void dispatchMessage(@NonNull Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    注释讲,这是处理消息的系统,看样子处理消息的核心思路就在这里了。我们开始逐行分析代码,这个方法首先判断了msg.callback是否为null,那么先看判断的具体内容是什么。我们打开Message的源码,发现了有这样的一个变量

       @UnsupportedAppUsage
        /*package*/ Runnable callback;
    

    所以callback的本质是一个Runnable接口。那么它什么时候才不为空呢,我们查看Message的源码,经过关键字搜索可以看到如下几个地方可以赋值。
    1.静态的Message构造方法

     public static Message obtain(Message orig) {
            Message m = obtain();
            m.what = orig.what;
            m.arg1 = orig.arg1;
            m.arg2 = orig.arg2;
            m.obj = orig.obj;
            m.replyTo = orig.replyTo;
            m.sendingUid = orig.sendingUid;
            m.workSourceUid = orig.workSourceUid;
            if (orig.data != null) {
                m.data = new Bundle(orig.data);
            }
            m.target = orig.target;
            m.callback = orig.callback;
    
            return m;
        }
    

    2.Obtain()方法

     public static Message obtain(Handler h, Runnable callback) {
            Message m = obtain();
            m.target = h;
            m.callback = callback;
            return m;
     }
    

    3.setCallback()方法

     @UnsupportedAppUsage
        public Message setCallback(Runnable r) {
            callback = r;
            return this;
        }
    

    所以可以通过调用Message中的这几个方法来手动设置callback。
    继续看代码,当msg.callback不为空时调用handleCallback()方法,那么我们看一下handleCallback()方法。

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

    代码很简单,就是运行了Runnable接口的run()方法。
    那么当msg.callback为空时,开始判断mCallback是否为空。我们开始查看mCallback的来源。

       @UnsupportedAppUsage
        final Callback mCallback;
    

    通过查看handler类,赋值的方式有下面几个地方
    1.Handler构造方法

     public Handler(@Nullable 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 " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    2.Handler的构造方法

    @UnsupportedAppUsage
       public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
           mLooper = looper;
           mQueue = looper.mQueue;
           mCallback = callback;
           mAsynchronous = async;
       }
    

    因此在创建Handler实例时我们可以设置mCallback,那么我们接着看,当它不为空时,执行mCallback.handleMessage(msg),那我们回头看一下这个Callback接口的定义。

        public interface Callback {
            /**
             * @param msg A {@link android.os.Message Message} object
             * @return True if no further handling is desired
             */
            boolean handleMessage(@NonNull Message msg);
        }
    

    当我们总结的msg.callback和mCallback都为空时,执行handleMessage(msg)方法。所以我们大致梳理一下,执行消息的核心判断条件就是Message中的callback或Handler中的mCallback是否为null。我们接下来看消息如何被检测到并开始处理的。所以我们通过dispatchMessage()方法查找消息如何被分配的.
    这里比较难找,我在看的过程中通过仔细对比才发现真正调用消息分发的地方在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();
    
           // 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) {
                   // 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);
               }
               // Make sure the observer won't change while processing a transaction.
               final Observer observer = sObserver;
    
               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;
               Object token = null;
               if (observer != null) {
                   token = observer.messageDispatchStarting();
               }
               long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
               try {
                   msg.target.dispatchMessage(msg);
                   if (observer != null) {
                       observer.messageDispatched(token, msg);
                   }
                   dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
               } catch (Exception exception) {
                   if (observer != null) {
                       observer.dispatchingThrewException(token, msg, exception);
                   }
                   throw exception;
               } finally {
                   ThreadLocalWorkSource.restore(origWorkSource);
                   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);
               }
    
               msg.recycleUnchecked();
           }
       }
    

    这里的代码较多,涉及到了未向开发者开放的一些接口,所以我们只关注如何调用分发消息,这个方法里有一个死循环,即for(; ; ),这种写法比while(true)更好,其理由涉及到了一些底层的知识,有兴趣的可以自行去查阅,这个方法里,通过将Looper对象里所定义的MessageQueue对象循环遍历,取出消息,再通过每个Message种所定义的Handler实例对象target,来调用dispatchMessage(),从而实现消息的获取。
    我们再分析消息的发送,我们常用的发送消息的方法是sendMessage(Message),那么我们就从这个消息开始看,通过sendMessage(Message)的不断查找,我们最终在MessageQueue对象里发现了如何发送消息

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

    这个方法核心思路就是通过判断传来的Message对象中,根据Handler是否存在和所处的子线程是否在运行来判断是否将该Message对象放进消息队列中,至此我们差不多就了解了整个消息发送和处理消息的原理了,我们简单总结一下:

    • 在创建Handler时,我们复写了handleMessage(),用来做消息处理的具体实现。
    • 当在子线程创建Handler时,我们需要手动调用Looper.prepare(),和Looper.loop(),loop()中包含一个死循环,遍历消息MessageQueue,主线程时不需要,因为ActivityThread中已经帮我们做了这步操作。
    • 当我们调用到sendMessage时,Message会根据Handler是否创建和线程是否保活来确定是否将该Message加入消息队列中。
    • 当消息进入队列以后,符合条件的会执行Handler中的dispatchMessage方法,判断Message是否设置了callback,如果设置了则调用callback.run(),或Handler是否设置了callback,如果设置了则调用handleMessage()方法。至此我们的消息就成功的被处理了.
      所以我们思考一下,能否让多个Handler同时处理一条消息?
      答:当然可以,因为Looper遍历了属于自己的MessageQueue,那么我们只要在创建handler时,将多个Handler的Looper设置成一致就可以了~因此我们也就延申出了线程通信的方式,通过Looper的设置来实现线程间的通信。

    相关文章

      网友评论

        本文标题:Android SDK源码解析篇之Handler如何实现发送和处

        本文链接:https://www.haomeiwen.com/subject/tcbbqhtx.html