再论Handler—Handler事件分发、Handler线程切换原理解析
#再论Handler—Handler原理解析
标签(空格分隔): Android架构师之路
Handler,无论你菜鸟还是像我一样的老菜鸟,都无法避免这个亘古不灭的话题,只有了解学会Handler的原理,你才能变成一个出的厅堂下得厨房的优秀的老菜鸟。其实同类优秀的帖子已经很多了,但是为了强化记忆我的理解,还是想再次记一下,我心中的Handler。
要介绍Handler,就必须要介绍与它息息相关甚至“同为一体”的其它几个类:Looper、MessageQueue、Message。所以本篇文章就分为三个部分:
- Handler、Looper、MessageQueue、Message原理及关系。
- Handler事件分发原理。
- Handler线程切换原理。
1. Handler、Looper、MessageQueue、Message原理及关系。
1.1 Message
Message是定义一个包含描述和可以发送到Handler的任意数据对象的消息。
我们先看Message的几个关键变量:
target,就是负责发送且分发它的Handler;
callback,是一个Runnable回调;
next,下一个消息池中的它之下的message。
现在有3个问题:
- 1.target从哪来来,作用是什么?
- 2.callback又是什么及其作用;
- 3.还有next是什么?
我们先不着急给出答案,接着往下看。
1.2 MessageQueue 消息队列
它持有并维护了当前线程的Looper分发的消息列表。消息并不是直接而是通过Handler 关联的Looper入队列的。
MessageQueue看上去是消息队列,然而它实际上只是消息队列的管理者。消息列表是它持有的Message mMessage,是由Message组成的单链表结构。
我们看它管理消息的两个核心方法:
- boolean enqueueMessage(Message msg, long when) ;
- Message next()。
1.2.1 boolean enqueueMessage(Message msg, long when) ,消息入队列;
先上代码:
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } 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; }
可以看到,在第一行就校验了msg.target是否为null,由此可知,msg必须关联(持有)负责分发它的handler;
最重要的是for循环里入队的这部分,通过逐个和消息列表里消息的发生时间对比,找到与当前入队的message的 -分发时间-之前与它时间最接近的preMsg,将mesage设置到preMsg的next(此处也解答了问题3:message的next是什么),最终得到的是一个按时间先后顺序排列的链表;
此外,入队操作还做了一件事情:当消息入队时候,如果队列为空的或需要立即分发或当前消息分发时间-早于消息队列中最近一个需要分发的消息的时间的(而此时MessageQueue的next()方法的循环是处于阻塞状态的),就调用一个native方法nativeWake()唤醒取出消息的next()(next方法的循环会被nativePollOnce()native方法阻塞)循环。
1.2.2 Message 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(); } //-1-直接休眠一定的时间; 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; //-2-找出下一个需要分发的消息。 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) { //-3- 如果时机未到,即当前时间还不到消息的处理时间,就计算休眠时间, //反之,返回这个待处理的消息。并将休眠状态mBlocked置为false 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. //-4-只有当消息队列是空的或者现在还不到待处理的消息的时间的时候,idleHanler(空闲handler)才会被执行 if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } //-5-如果没有空闲Handler待处理(消息队列也没有需要处理的message),那么,标记一下循环被阻塞了。 if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } //-5-将idleHandler列表转为数组保存 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. //-6-开始处理IdleHandler的事务。 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); } //-7-如果IdleHandler的queueIdle返回false,就不需要保持,这执行完一次任务后直接移除,反之, //后面每次空闲的时候都执行 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; } }
看到这段代码,愈发觉得谷歌工程是真的是厉害,不知道秃到什么程度才能写出这么精妙的代码,哈哈哈哈哈。由于这段代码我看着太喜欢了,就按照注释逐个解释:
- 1-这段代码上来不管三七二十一就调了native的休眠方法——nativePoolOnce()(正好对应了消息入队的nativeWake()),如果没有消息处理的时候,next()方法就会阻塞在这里。当然休眠时间nextPollTimeoutMillis为0的时候眼睛一闭一睁也没有意义啊。
此时,突然有一个问题——循环阻塞在这里了,主线程不就卡住了嘛?为什么没有ANR?
具体原理,可以查看这篇帖子还有这篇帖子等等很多优质讲解。简而言之调用native的nativePoolOnce方法时候,调用了linux低层,使当前线程腾出CPU的使用权,处于休眠等待状态,直到休眠时间结束或消息入队被唤醒。
- 2- 通过内循环从消息队列中寻找出下一个有效的message;
- 3- 判断是否到了上一步得到的message的处理时间,如果到了时间,就返回这个message(返回给Looper的调用处,然后分发该事件);如果没有到,计算需要休眠的时间。
- 后面几点其实不在我们讨论内容,可以直接跳过
- 4- 在3处,没有返回message,就以为着:1.要么现在还没有到下一个消息分发的时间;2.要么当前消息队列是空的。此时开始处理空闲handler(IdleHanler)的事物。
- 5- 这一步将IdleHandler列表转为数组存储。为什么要转数组,而不是直接处理列表?此处我需要引入一段代码:
private IdleHandler[] mPendingIdleHandlers;private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); /** * Add a new {@link IdleHandler} to this message queue. This may be * removed automatically for you by returning false from * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is * invoked, or explicitly removing it with {@link #removeIdleHandler}. * * This method is safe to call from any thread. * * @param handler The IdleHandler to be added. */
public void addIdleHandler(@NonNull IdleHandler handler) { if (handler == null) { throw new NullPointerException("Can't add a null IdleHandler"); } synchronized (this) { mIdleHandlers.add(handler); } } /** * Remove an {@link IdleHandler} from the queue that was previously added * with {@link #addIdleHandler}. If the given object is not currently * in the idle list, nothing is done. * * This method is safe to call from any thread. * * @param handler The IdleHandler to be removed. */
public void removeIdleHandler(@NonNull IdleHandler handler) { synchronized (this) { mIdleHandlers.remove(handler); } }
可以看到,这个列表拥有add和remove的方法,这两个方法会在外部随时被调用,也就意味着,列表是变化的,如果一直添加的话,就会不停的处理,当前循环就会在这里耗时,消息队列的消息就有可能不能被及时处理了。
- 6- 正式开始迭代处理idleHandler
- 7- 我们先引入IdleHandler代码:
/** * Callback interface for discovering when a thread is going to block * waiting for more messages. * 当线程即将进入阻塞状态的时候需要处理的更多消息 */ public static interface IdleHandler { /** * Called when the message queue has run out of messages and will now * wait for more. Return true to keep your idle handler active, false * to have it removed. This may be called if there are still messages * pending in the queue, but they are all scheduled to be dispatched * after the current time. * 当MessageQueue的消息被执行完或下一个消息需要等待时间的时候会被调用。 * 如果仅需要被调用一次,返回false,则被调用一次就会从列表中移除此项,如果需要每次空闲 * 时都需要被调用就返回true,则不会被移除。 */ boolean queueIdle(); }
IdleHandle是当线程空闲(MessageQueue中暂时不处理消息时)时才需要处理的事物。使用方法 ,使用场景我不再赘述。
- 7-总之,如果IdleHandler的queueIdle()放回fasle,就在执行完之后移除掉此项,反之不会被移除,且下一次线程空闲的时候还会被调用。
好了,next()方法分析完了,再次揪一揪自己浓密健壮的头发感叹——谷歌工程是真的厉害!
1.3 Looper
Looper短短不到500行的代码,并没有什么特殊之处,它用于为一个线程运行一个消息循环。新的线程默认是没有消息循环的,在线程中调用prepare可以产生一个消息循环,然后调用loop方法可以开始运行循环直到线程终止。
Looper中有以下几个重要的方法:
- ThreadLocal sThreadLocal ;
- prepare();
- loop();
1.3.1 sThreadLocal
sThreadLocal 存放了当前线程绑定的Looper,除非先调用了prepare(),否则调用ThreadLocal的get方法时会返回null;
1.3.2 prepare()
那么我们看看prepare()方法:
/** 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()}. *初始化当前线程的Looper,这给我们一个机会去创建引用这个Looper *的Handler,要真正开始循环,确保调用了loop方法。 */ public static void prepare() { prepare(true); } 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)); } /** *Looper构造函数 */ private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
从代码及其注释可以看出来,它为当前线程初始化一个唯一(多次调用该方法会抛错)的Looper,并保存到sThreadLocal中,同时,会在其构造函数中,初始化一个唯一的MessageQueue。
1.3.3 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; //…… for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } //…… try { //事件分发 msg.target.dispatchMessage(msg); //…… } catch (Exception exception) { //…… throw exception; } finally { //…… } msg.recycleUnchecked(); } }
这个方法的作用十分简单:
维持一个死循环。在循环中不断的从当前线程的MessageQueue中取出一个待分发的消息。
Message msg = queue.next(); // might block
注意这行代码,表示循环在这里可能会阻塞。当消息队列中没有消息的时候,循环就会阻塞在MessageQueue的next方法中(对应MessageQueue的next())。
然后调用message.target.dispatchMessage()方法执行事件。
1.4 Handler
1.4.1 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的构造函数或无参的构造函数被调用时,默认绑定当前线程的Looper,这个Looper存于Looper的sThreadLocal中(注意这个不一定存在),并且会校验Looper是否存在,如果不存在,就会抛出异常.
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; }
2.而有Looper的构造函数则关联传入的Looper,而这一点与Handler的线程切换至关重要。
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); }
另外,我们还需要注意构造函数中的Callback,初始化的时候也可以选择性传入,具体作用,我们后面讲
1.4.2 Handler发送消息的方式
除了postAtFrontOfQueue方法和sendMessageAtFrontOfQueue方法,其它方法最后都调用了sendMessageAtTime方法。不过殊途同归,最终这三个方法都调用了enqueueMessage方法,最终调用到了当前线程的MessageQueue的enqueMessage方法
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); }
看到这段代码,我们看到一个熟悉的东西——target,不管是post还是send方式,最终都会在这里将当前发送该message的handler注入到message中,这样,就解答了问题1:message的target从哪里来的。
在此有必要说明一点post方式和send方式一个不同之处——post方式发送的message会被注入我们初始化的Runnable回调,这就解答了我们前面的问题2:message的callback从哪里来的?
Handler,Message,MessageQueue,Looper的相关知识已经介绍完毕,下面开始正文了!
2. Handler事件分发原理
public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); // Install selective syscall interception AndroidOs.install(); // CloseGuard defaults to true and can be quite spammy. We // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false); Environment.initForCurrentUser(); // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir); Process.setArgV0("" );// -1-初始化Looper Looper.prepareMainLooper(); // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line. // It will be in the format "seq=114" long startSeq = 0; if (args != null) { for (int i = args.length - 1; i >= 0; --i) { if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) { startSeq = Long.parseLong( args[i].substring(PROC_START_SEQ_IDENT.length())); } } } 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); //-2-开始循环 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
每每看到这段代码,我都有同样的感触。那是16年的初夏,就像我16岁的雨季,在android成长路上充满了懵懂和无知。当时面试的时候有一位面试官问题,android有没有main方法,我说有的,我看到过,她当时像笑傻子一样。那时候我还对android知之甚少,只知道初次看activity代码的时候,找不到main方法我就郁闷怎么启动呢,百度了一下找到了这个main方法……面试的时候我还和面试官争辩了几句……时至今日,细节我已经忘记,但是我还是想起了她,因为她笑起来可好看,而今天是七夕……
哈哈哈,言归正传,我们接着说这个方法。
step1 初始化Looper
在ActivityThread类的main方法中:
Looper调用了它的一个特殊的方法——prepareMainLooper,专门为主线程准备的,用于初始化主线程的Looper;初始化完毕后,调用Looper的loop方法,启动Looper,的loop,开始消息分发。
step2 初始化一个用于事件分发的Handler
结合我们前面讲到的,handler没有Looper的构造函数调用的时候,会选择性注入一个callback;同时会默认绑定当前线程的Looper。这也就是为什么我们在主线程可以new Handler()然后直接发送消息的原因。
而在子线程,若像下面这种方式:
new Thread(new Runnable() { @Override public void run() { new Handler().post(() -> { // TODO: 2020-08-25 todo sth }); } }).start();
一运行显然是会失败的,会抛出 “Can’t create handler inside thread ThreadName-x that has not called Looper.prepare()”。我们前面讲Handler构造函数的时候说过,初始化Handler的时候需要关联一个Looper.子线程并没有初始化Looper,怎么办呢?有两种方式:一,我们自己调用Looper的prepare方法初始化一个,然后初始化Handler的时候就会自动关联;二,传入其它线程的Handler,比如主线程的handler。
public void startNewThread() { //仅用于示例,不要模仿我这种Thread写法 new Thread(new Runnable() { @Override public void run() { //方式一 Looper.prepare(); new Handler(getMainLooper()).post(() -> { // TODO: 2020-08-25 todo sth }); Looper.loop();// Looper.myLooper().quitSafely(); //方式二 new Handler(getMainLooper()).post(() -> { // TODO: 2020-08-25 todo sth }); } }).start(); }
切记!调用完Looper.prepare()方法后记得调用Looper.loop()方法。另外,在子线程任务结束后,调用 Looper.myLooper().quitSafely()退出。
step3 发送一个消息
我们通过post方式发送一个消息,紧接着在后续方法中给message注入当前handler,然后通过当前Handler关联的Looper,获取当关联的Looper中的MessageQueue,再然后,调MessageQueue的enqueueMessage方法消息入队。
step4 分发一个消息
消息入队之后,我们就该分发消息了。
我们再次定位到Looper的loop()方法中:
for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } //…… try { //事件分发 msg.target.dispatchMessage(msg); //…… } catch (Exception exception) { //…… throw exception; } finally { //…… } }
在loop循环中,通过MessageQueue的next方法取出待分发的消息,然后调用
msg.target.dispatchMessage(msg);上面我们已经知道,target就是message被发出入队时被注入的Handler,也就是说,负责发出这个message消息的handler,还负责分发它!我们走进Handler的dispathchMessage方法:
/** * 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); } } private static void handleCallback(Message message) { message.callback.run(); } /** * Subclasses must implement this to receive messages. */ public void handleMessage(@NonNull Message msg) { }
第1部分:如果msg的calback存在,即通过post方式初始化注入的Runnable存在,那么调用这个Runnable处理消息;
第2部分:如果mCallback存在即初始化Handler的时候初始化了Callback,就调用mCallback的方式处理message。
第3部分:如果前两个回调都不存在,就调用Handler的handleMessage方法处理消息。而我们也看到了,这个方法是一个空的实现,这就需要我么自己去实现它。这也是为什么我们平时使用handler的时候经常会实例化Handler的子类而不是Handler本身的原因。
– 到此为止,Handler的事件分发原理终于讲完了……
3. Handler的线程切换原理。
我之前看过很多关于Handler的帖子,几乎都是在讲handler事件分发,本质上,事件分发和线程切换原理是一样的,但是,两者侧重点不同!两者都包含了消息机制的整个流程,很多人在讲述的时候,也把整个过程混为一谈;也有人把线程切换错误的认为是由handler完成的,实际上他是一叶障目只看到了冰山一角,根本没有看到重点,线程切换的实质和Handler根本没有半毛钱关系!!
**线程切换并不是由Handler完成的,线程切换的重点是Looper!**非要说和Handler有关系,那么只能说Handler只充当一个送快递的作用。
接下来,我来为你细细分解:
我们回溯从handler、looper的初始化到发出一个message,最后到message分发完成整个流程:
- 创建一个Looper,内部初始化一个MessageQueue,运行looper;
- 初始化一个Handler;
- 发送一个消息,消息入队;
- 消息出队,分发一个消息;
在讲Handler的构造函数时,我们知道,Handler会默认关联当前线程的Looper或传入的Looper,这点至关重要!为什么至关重要呢,因为:传入的不同线程的Looper,发送的消息最终被分发到的线程就不同!Message最终被处理的线程就是负责发送和分发它的Handler初始化的时候关联的Looper所在的线程!
是不是有点郁闷,我们接着往下捋:
handler通过post或者send方式最终都调用了下面两个方法:
public boolean sendMessageAtTime(@NonNull 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); } public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) { //消息队列 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, 0); }
没错,这就是前面说的入队的方法。但是你有没有一瞬间产生过疑问——mQueue从哪里来的???
往上翻,定位到Handler的两个构造方法,赫然看到一行:
mQueue = mLooper.mQueue;
看到没有,Handler的MessageQueue来自关联的Loooper,也就是我们前面说的,初始化Looper的时候默认初始化的MessageQueue!再回想我上面说的,Looper的prepare()方法为每个线程初始化一个唯一的Looper和MessageQueue!
到此,有没有想到什么?
我们再沿着流程往下,消息入队,然后由Looper的loop取出消息,最后被分发!
可以说,**所谓线程的切换,就是把消息插入不同线程的Looper内的MessageQueue中!**消息分发就是执行在不同线程的Looper的loop()方法中的,自然而然的,消息被处理的时候就已经在不同的线程中了。自此,线程也就切换完成了!而handler,开始是一个负责将消息送入队列的快递员,最后,又只起了一个回调的作用!
到此,Handler线程切换哦不,Looper的线程切换原理已经讲完了,最后总结两点:
- 线程切换和事件分发本质是一样的;
- 线程切换是有Looper完成的,Handler只充当一个回调的作用。
总结
好了,大功告成,这篇帖子到此为止了。描述顺序和逻辑可能有点乱,还请见谅。如有不足,欢迎批评指正,非常感谢:)。
更多相关文章
- Android中View的绘制机制
- Android的BUG(一) - HTML 5 播放streaming video造成卡住的问题
- Android中Toast显示时间的自定义
- 在 Android(安卓)上,一个完整的 UDP 通信模块应该是怎样的?
- Android(安卓)中的线程池
- UI效果(9): 屏幕相关操作
- Android异步任务的使用方法
- Android(安卓)黑科技保活实现原理揭秘
- 一起来学Android(安卓)Studio:(五)导入 jar 包和第三方开源库方法