Handler

简述

Handler大家不陌生,但是其中的细节还是有不少的,其中有些知识点还是很重要的,我们一起搞一下。

Handler到底是个什么东西

handler是Android消息传递模块中必不缺少的一样,在App启动时,也不乏看到他的身影。比如在ActivityThread中的main方法里就会有主线程Handler的初始化。和主线程looper的初始化。

class ActivityThread {
 public static void main(String[] args) {
       
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

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

Handler 使用的简单流程

当我们使用Handler在不考虑内存泄漏的情况下,一个简单的例子是这样的。

class SecondActivity : AppCompatActivity() {

    companion object {
        var TAG = SecondActivity::class.java.name
    }
    var handler = Handler(object:Handler.Callback{
        override fun handleMessage(msg: Message): Boolean {
            Log.d(TAG,"handle")
            return false;
        }

    })
fun sendMessage () {
        var message = Message.obtain()
        handler.sendMessage(message)
    }
}

一般情况下,handler是处理的是不同线程之间的通信。

Handler Looper MessageQueue 与线程的比例关系

我们知道Handler是我们自己new出来的,所以一个线程里可以有无数个Handler,但是子线程使用Handler的时候要调用一下Looper.prepare()方法,这个东西没有的话,handler是不能正常工作的。

 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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));
    }
/**
Handler初始化时候会找Looper,looper没有初始化会报错。
**/
 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;
    }

由此可见Looper与线程的关系是1:1的。而MessageQueue是在Looper里取得,所以Handler Looper MessageQueue 在一个线程中的比例是n:1:1

Handler 内部消息通信流程

当消息从handler发出的时候有两个方法sendMessage 和post等一系列类似方法,最后都会到

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
boolean enqueueMessage(Message msg, long when) {
        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;
                    // 寻找适应p.when>when的 message,之后插入  
                    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按时间排了个序,也就是说message现在是一个按时间排序的优先级序列 。这块暂时结束了,接着看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();

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

           
            // Make sure the observer won't change while processing a transaction.
            final Observer observer = sObserver;
            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);
                }
            }
            msg.recycleUnchecked();
        }
    }

looper就干了一件事情,开启一个死循环,将msg从queue中读出来,然后msg.target.dispatchMessage(msg)这样我们的handler就收到个回调。

关于队列中没有消息的情况下

试想一下,消息不可能一直有的,肯定有时候队列中没有消息,那么这个时候如果还一直循环调用的话,十分的消耗资源,而且完全没用。Android肯定不会这样做的,他在looper中有这么一句注解,在取得queue.next的时候旁边注释已经有提示了might block

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();
            }
            //根据nextPollTimeoutMillis 的值判断是否要挂起线程
            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 {
                    // 这里没有message时候,下次进循环就挂起
                    nextPollTimeoutMillis = -1;
                }

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

      
        }
    }

具体实现就是在nativePollOnce 这个native方法里,这里不展开说了,只需要知道-1的时候这个方法将线程挂起即可,之后有消息之后程序接着运行,和object.wait/ notify类似。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一、google为什么设计这套机制 主要是为了解决在非UI线程中更新UI组件比较麻烦的问题。 二、google如何...
    可爱的肥脸阅读 688评论 6 6
  • 一、前言 在 Android 开发中,handler 机制的使用几乎随处可见,作为面试中的常客,我们真的了...
    阿西糖阅读 717评论 0 2
  • 谈到Android开发,就离不开线程操作,而我们需要在子线程中更新UI,一般有以下几种方式: 1、view.pos...
    StChris阅读 1,348评论 0 1
  • Handler是什么 先来看官方文档对Handler的描述A Handler allows you to send...
    ywy_袁滚滚阅读 868评论 0 3
  • 前言 在Android开发的多线程应用场景中,Handler机制十分常用 今天,我将手把手带你深入分析Handle...
    BrotherChen阅读 486评论 0 0