Android Handler机制源码解析和使用MessageQueue

第一次写博客,没有别的目的,就是把自己理解的知识进行一个梳理,方便下次需要的时候不用到处去找.

要理解handler 机制需要知道这几个类,简单的介绍下

1,handler  用于线程之间切换,发消息 处理消息(类似一个快递员,收快递,发快递)

3,Message 一个消息对象,他有个tag,还有个object,还有两个args(可以把它理解为一个快递包装起来了,tag就是它的收件人名字,我们发了一百条消息,当handler 收到的时候根据tag 去区分它们都是做什么用的,object,和args1 代表它实际的货物,也就是我们要传过去的data)

3,MessageQueue 见名知意 一个消息队列(其他它内部并不是队列实现的,而是一个链表,不过你可以这样理解,它就是一个快递仓库,里面有各种各样的货物(message))

4,Looper   一个阻塞式轮询器,它作用就是不断的从MessageQueue中拿出来消息,然后给分发给对应的handler(类似一家快递公司,不断的去看仓库(MessageQueue)是否有快递发货(message),如果有马上把快递(message)拿出来,发给对应的快递员(handler))

简单介绍了下,不要认为我送过快递,我没做过快递员,当然我不鄙视任何职业,只是为了方便理解,ps(我记知识的方法,会把这一套逻辑联想为一套自己已经熟悉的逻辑,下次想的时候想起自己熟悉的逻辑就知道了)


进入正题

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

这块代码我们只需要关心第11 行之后, 这个大概意思呢就是 一个快递员(handler) 创建,必须要给她一家快递公司挂名啊(looper),

然后公司(looper)把管理的仓库(持有的MessageQueue)交给这个快递员(handler),

Looper.myLooper();

至于公司怎么创建先不需要关心,等下会说,

我们只需要明白这个方法过后 快递员(handler)有公司(looper),有仓库(MessageQueue),这样才能发送快递呀.这里边有一个非空判断,很好理解 假如说没有这个快递公司,其他都是白扯 只能让程序停止了,下面有好多类似的判断在这里一带而过,不再多做解释.

ok,下面我们看发快递(发消息)的方法

sendMesssage 一系列的方法和 post一系列的方法 内部最终实现都是这个方法sendMessageAtTime()

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

这个方法很简单快递员(handler)发货之后,不能每个快递员(handler)都有自己的发货方式,A发的只有货物,B发的货物,还说要晚两天发,这样人家很难管理所以要做到统一对接,走一个正规的流程.

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

注意2行,,货物需要登记,不是直接扔仓库就完了,出事了以后还得找人负责呢,所以通过货物(Message)的tag ,这里把当前的快递员(handler) 做了一个登记

再看第6行,这就是一个货物(Message)入库的方法,我们去看下源码

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

这段代码有好多东西是很难看得懂的,但是这个并不影响我们对这个方法的理解,(ps我感觉他写的很复杂,一点都不通俗易懂)

大概意思就是把货物(Message)入库,进行处理,根据发货时间排序一下.

1MessageQueue队列里边只保留一个Message对象,

2刚发过来的Message对象和之前持有的Message比较,根据发送延迟时间排列一下(持有那个延迟最低的那个Message),

3,把多余的对象通过Message对象自己内部的Next连接起来,简略的说把Message对象通过next方法连接起来,然后根据时间顺序 排序一下.

到此为止,消息已经从子线程的handler对象发送到消息中并且按时间顺序排好

再看最后一个公司(looper)类 先看构造函数

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

可以的到两点有用的信息1,每个公司(looper)对象会创建一个仓库(MessageQueue对象)2,每个公司(looper)会持有当前的线程对象(这里标记一下)

再看最重要的流程

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

大概意思就是公司(looper)对仓库(MessageQueue)里面的货物(Message)进行处理,也是整个环节中比较重要的流程,你想如果一直收货(接受Message)却不发货(处理Message) 仓库(MessageQueue)终会爆棚,现在说一下具体怎么处理的

主要看13行之后,开启了一个阻塞式死循环,ps这个我解释不了,只你告诉你这么记了,他的功能基本上就是无限循环检查仓库(MessageQueue)是否有货物(Message),如果有看第36行,

  msg.target.dispatchMessage(msg);

根据货物(Message)的tag(handler) 去把货物(Message)分发出去,发到快递员(handler)内部去处理,至此又把货物(Message)发回给快递员(handler对象),只不过在这个过程已经切换了线程, 多条线程操作一条仓库(MessageQueue).

我们再去看一下快递员(handler)怎么处理的

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

这个方法这样理解,消息本身有回调优先级最高,最先回调,然后再是handler对象的callback (这个从上边构造函数的时候可以看到在哪里赋值)优先级第二, 最后才是自己的handMessage方法被回调,优先级最低.

      其中有两个问题我们还没说,现在开始解释

第一 公司(looper对象)的创建,看源码可知,构造方法私有,只有内部调用看到这个方法

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

这个方法首先是一个判断,如果get到的对象不为空 就抛异常,大概意思你要创建公司(looper),我先判断你这个公司是不是存在,存在直接告诉你不行,有这个公司了,如果没有这个公司,我就给你创建,然后在我这边登记一下,结论 每个线程只能有一个looper对象.(重点标记)SthreadLocal 这个对象可以把它理解为一个map集合,那么key是什么呢,聪明的人应该能知道了,就是CurrentTread,下面验证一下

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



 public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

看到这里就差不多了,ps(看深了容易乱,就像你知道1+1=2,却不知道为什么等于2 ,这个不是我们研究的,这个是科学家去研究的,如果想当科学家的另当别论)

这个类就相当于公司登记,利用一个map集合,当前线程做Key,公司(Looper)对象做为值保存,方便获取和存储,至此我们基本上明白了,(ps回看一下handler的构造方法 获取looper ,你发现就是这样获取looper对象的,)

最后总结一下 

 1Looper构造方法私有,必须调用 prepare()方法才能创建出来否则 looper对象就是空的,你创建handler的时候就会抛异常(回看上边)

2,Looper创建的时候还需要先验证一下是否存在(根据当前线程做Key去验证),所以每条线程只允许创建一个looper对象,由于MessageQueue对象是在looper对象构造方法创建的可知,每个looper只有一个MessageQueue,也就是每个线程只有一个队列,

3,handler  和Message可以有多个

小知识 

View.Post() 方法 activity.runOnUiThread()方法,handler.post  ()

都是用的handler机制sendMessage()实现的线程切换,喜欢的自己去看源码

最后画张图看一下



猜你喜欢

转载自blog.csdn.net/qq_37488573/article/details/79761476
今日推荐