美文网首页
Handler运行机制

Handler运行机制

作者: 颜凉文丑 | 来源:发表于2020-03-12 14:29 被阅读0次

Android 消息机制就是handler的运行机制以及MessageQueue和Looper的工作过程。

那么Android的消息机制为什么要用Handler呢?
在Android中,UI线程是非线程安全的,所以规定只能在主线程中访问UI线程。而且主线程中不能进行耗时的操作,所以把耗时操作放在异步线程中执行。那如何让子线程中的获取的数据在主线程中进行使用呢,这个时候就用到了Handler。

Handler的运行机制 少不了Handler Message MessageQueue Looper几个类的支撑,下面分别对每个类进行介绍。

Handler

A Handler allows you to send and process {@link Message} and Runnable objects associated with a thread's {@link MessageQueue}. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread /message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
大致意思:handler 通过关联一个消息队列来发送和处理消息,发送或处理Runnable对象

Handler的作用

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
大致意思:handler 通过关联一个消息队列来发送和处理消息,发送或处理Runnable对象

  • 在未来某个时间点执行分发的message/runnable
  • 在其他线程执行action

介绍完handler 来看看handler的 constructor的实现

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

首先检测用户创建的handler类是不是static的,如果不是则会提示The following Handler class should be static or leaks might occur,意思就说会有导致内存泄露的可能。
在子线程中如果没有创建looper,则会抛出异常“has not called Looper.prepare()”,因为在主线程中,系统默认就会创建looper,所以我们平时使用handler时,没有创建looper是不报错的,但是在子线程中会抛出异常的,接着就是将handler和MessageQueue进行关联。

Handler的主要工作包含消息的发送和接收,先分析发送:

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

  public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }

  public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

  public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }
  //上面的方法最终都会走到这里,这个方法主要功能还是实现了enqueueMessage
  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);
    }

接着分析enqueueMessage:

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

可以看到,Handler发送消息的过程就是向MessageQueue中插入一条消息,下面会讲到MessageQueue通过next方法获取出消息,然后返回给looper,在looper的loop方法中执行了msg.target.dispatchMessage(msg); msg.target 就是指的handler。那么接着分析dispatchMessage:

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

Message的callback就是一个Runnable对象,日常使用handler时会重写handlerMessage方法去处理消息,而callback的意义就是当我们不想实现handlerMessage方法时,就可以用callback代替。

Looper

used to run a message loop for a thread 简单的说就是 为一个线程进行消息轮询,典型的使用案例如下代码所示:

  * <pre>
  *  class LooperThread extends Thread {
  *      public Handler mHandler;
  *
  *      public void run() {
  *          Looper.prepare();
  *
  *          mHandler = new Handler() {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *
  *          Looper.loop();
  *      }
  *  }</pre>

在创建handler之前执行Looper.prepare(), 创建handler之后执行Looper.loop()即可。

    /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        prepare(true);
    }

作用时在确定执行轮询(loop)之前,创建handler并关联looper,执行prepare后确定执行loop,结束用quit。接着跟踪下:

prepare(boolean)方法

这里面的入参boolean表示Looper是否允许退出,true就表示允许退出,对于false则表示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));
    }

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
 public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

上面是子线程创建handler必须要调用的,后者是应用创建主线程时调用,使用者可不理会。关于sThreadLocal 后面文章会继续讲解。接着看下Looper的构造方法:

Looper(boolean)构造函数

private Looper(boolean quitAllowed) {
        // 创建MessageQueue对象
        mQueue = new MessageQueue(quitAllowed);
        // 记录当前线程
        mThread = Thread.currentThread();
    }

通过上面代码,我们发现就是创建了一个MessageQueue,并且把当前线程赋值给本地变量的mThread。这里就实现了Looper和MessageQueue,Thread的关联,也知道Looper类不是通过构造函数直接来创建looper,而是通过静态方法调用间接创建Looper类。前面我们讲了Looper的作用就是为线程轮询消息,接着看真正的实现:

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the 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;
        boolean slowDeliveryDetected = false;

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
...
            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 {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
    ...
            msg.recycleUnchecked();
        }
    }

通过queue.next() 获取消息,当消息为null的时候,MessageQueue就会调用quit或者quitSafety方法并被标记为退出状态,当消息队列被标记为退出状态时,next方法就会返回null。换句话说,loop方法会调用MessageQueue的next方法来获取新消息,next方法是一个阻塞操作,当没有消息时,next方法会一致阻塞,loop方法也会被阻塞,当有新消息到达时,Looper就会继续处理消息了。
msg.target.dispatchMessage(msg)分发消息,msg.target 就是指当前线程绑定的handler,msg.recycleUnchecked() 将消息放入消息池。

Message

Defines a message containing a description and arbitrary data object that can be sent to a {@link Handler}. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.
一个实现了序列化的对象。接着看一下obtion方法:

    /**
     * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
     * @param h  Handler to assign to the returned Message object's <em>target</em> member.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;

        return m;
    }

可以看到message.target = handler ,就可以理解了上面Looper.loop()方法分发消息的target 就是handler,可以知道先是handler发送消息,然后handler分发消息,最后自己实现handleMessage方法处理消息,其他obtain方法大同小异。

MessageQueue

MessageQueue主要包含两个功能,enqueueMessage和next。
先看一下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;
            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;
    }

从实现来看主要就是单链表的插入操作,接着再看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();
            }

            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;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 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方法中也有一个for循环,如果有新消息,next方法会retrun这条消息并且从链表中移除,否则的话会一直阻塞,直到有新消息。

总结:

  1. Looper类用来为一个线程开启一个消息循环。默认情况下android中新创建的线程是未开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环。)
    Looper对象通过MessageQueue来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。(如果对一个已经quit的Looper重新start会出现异常)

  2. 通常是通过Handler与Looper进行交互的,Handler可看做是Looper的一个接口,用来向指定的Looper发送消息及定义处理方法。
    默认情况下Handler会与其被定义时所在线程的Looper绑定,比如,Handler在主线程中定义,那么它是与主线程的Looper绑定。
    mainHandler = new Handler() 等价于new Handler(Looper.myLooper()).
    Looper.myLooper():获取当前进程的looper对象,类似的 Looper.getMainLooper() 用于获取主线程的Looper对象。

  3. 在非主线程中直接new Handler() 会报如下的错误:
    E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception
    E/AndroidRuntime( 6173): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
    原因是非主线程中默认没有创建Looper对象,需要先调用Looper.prepare()启用Looper。

  4. Looper.loop()让Looper开始工作,从消息队列里取消息,处理消息。注意:写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。

相关文章

网友评论

      本文标题:Handler运行机制

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