安卓消息处理机制 【安卓似懂非懂的小知识点】首篇
来源:互联网 发布:大数据 课程 编辑:程序博客网 时间:2024/06/05 18:28
前言:
因为Handler机制在网上很多,大家也分析的很透彻。但是还是有朋友会问我一些Handler/Looper的东西,那么本篇就以解惑为主,讲了一些平常我们可能没接触过的细节小知识点。后续大家也可以继续留言提问讨论。
首先简单概括一下Hanler机制的概况,我们通常怎么用,Hanler机制是怎么运作的,如图:
通常我们会先定义并初始化一个handler对象,然后在子线程做完一些操作需要通知Handler线程(大家熟悉的是在主线程,当然也可以是其他线程)操作时生成Message, handler.sendMessage ,就大功告成了。
我们来看看机制在做什么:
1、sendMessage (当然还有其他发送消息的形式)
handler调用此方法会将此message与自己绑定:msg.target = this; 标记这个message将来就是要执行此handler的处理。(对于这里,不一定是处理handleMessage,下面会有解释)
2、sendMessage最终调用MessageQueue.enqueueMessage
Handler有一个属于自己的MessageQueue(下面解释),MessageQueue将所有消息用一个链表维护,enqueueMessage会将消息按照执行时间等条件(除了执行时间还会有其他条件,下面解释)将消息放入链表等待Looper来取。
3、怎么执行到的handleMessage呢
Looper是在Looper所在线程中无限循环的,循环做什么呢?只有两件事,取message:queue.next(),执行message对应的操作msg.target.dispatchMessage(msg); 这个target就是前边说的message绑定的handler,也就是发送自己的handler,执行它的dispatchMessage里就是回调的咱们熟悉的handleMessage。
大致情况介绍完了,我们来详细分解一下:
首先大家疑惑较多的就是线程问题。我们什么都没有做Handler怎么就运行在主线程了?那我们就扩展开来,先看看Looper是怎么回事:
Looper:
首先贴一个demo:
@Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }
这个是我粘贴的HanlderThread的run方法,其实这就是我们通常使用Lopper的正确姿势。所以说,HanlderThread就是一个为我们封装好的包含有Looper的Thread。
简单说就是三部
1 Looper.prepare();
2 做一些loop前的操作,因为一旦loop就没有机会再执行线程中后续的代码了(除非调用quit),线程就进入了取消息,执行消息的无线循环中。
// 这个动作对应在HanlderThread里就是onLooperPrepared();这个回调方法,使用者override次方法,在这里做loop前的准备工作。
3 Looper.loop();
这里是Looper的初始化
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 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)); } private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
可以看到,prepare时new Looper放在ThreadLocal里,每次prepare时先判断ThreadLocal里有没有Looper,保证了Looper的唯一性,同时使用ThreadLocal又保证了线程之间不收干扰。
也就是说每一个线程都可以创建自己的Looper,且线程中唯一。
除此之外,Looper还提供了主线程标识:
private static Looper sMainLooper; // guarded by Looper.class public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } }
主线程是通过prepareMainLooper初始化的,我们可以通过getMainLooper方便的获取主线程Looper。prepareMainLooper不提供开发者使用,因为主线程Looper一早就被系统初始化了,轮不到我们插手。。。
这是ActivityThread的main方法,在new ActivityThread thread.attach(我们的activity的初始化以及生命周期都在attach之后了)之前就已经prepareMainLooper了。
Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); Looper.loop();
顺便插一句,Activity的生命周期就靠这个Looper了,系统会在合适的时机发送消息,ActivityThread里维护有一个Handler,他根据消息执行各种动作,生命周期就在其中。
再回过头看一下Looper的构造,保存了一个MessageQueue和当前的Thread。从这里可以看出,Looper和MessageQueue是一一对应的,Handler是通过和Looper的绑定获取的Loop的MessageQueue。
然后就是让Looper动起来,loop方法:
public static void loop() { ... for (;;) { Message msg = queue.next(); // might block ... msg.target.dispatchMessage(msg); ... msg.recycleUnchecked();
捡两句贴一下,就是循环起来,取消息,执行消息,回收消息。
---------------------------------------------------------------------------------
然后再来看一些细节的东西,上面提到了几个地方说要下面讲,好的,面下好了,开讲:
1、消息以及消息发送
首先说一下消息怎么生成。
两种方法:1、直接new; 2、Message的obtain方法;3、Handler的obtainMessage(其实也调用的Message.obtain)
那么就重点说obtain
public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); }
Message里维护有一个消息池,sPool,obtain方法就是从这里取消息的,能够提高效率合理利用已有资源,建议大家这样使用。
回收消息 recycle:
取出消息 obtain
2、handler的回调处理动作,handleMessage,是Looper取到消息后通过消息message调用的handler的dispatchMessage回调的。
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
这里调用了三个回调,从代码逻辑可以看出:
1、handleCallback(msg)优先,有了他就屏蔽其他处理;
public static Message obtain(Handler h, Runnable callback)
Message提供了这个获取方式,生成消息时传入一个Runnable赋值给成员callback,开发者也可以自己直接赋值进去。有了这个callback,消息就唯一执行此callback的run方法,屏蔽其他handleMessage。(handleCallback是直接调用callback的run方法,并没有start启动线程)
另外,Handler的post方法也是生成了带有callback的message发送出去的。
2、其次是mCallback的handleMessage,根据其返回值决定是否执行handler的handleMessage;
Handler也提供了可以传入Callback的构造方法,这个Callback是Handler内部的interface,开发者需自行override其handleMessage,这个handleMessage返回值为true则屏蔽handler的handleMessage,返回false则执行完之后还会执行handler的handleMessage
3、最后才是handler的handleMessage。
3、Handler并不是只能运行在主线程中,那他到底运行在哪里,我们怎么做? 应用程序里有多少Looper有多少MessageQueue,他们怎么跟Handler联系起来的
前面讲Looper的时候说到了,Looper与MessageQueue一一对应,Handler是与Looper建立联系后才能拿到MessageQueue的。Handler是根据与自己建立联系的Looper去确定自己要运行在什么线程的,因为Handler的回调handleMessage是Looper取执行的嘛。
前面讲Looper也讲到,Looper与线程相关,也就是每个线程都可以创建自己的Looper且唯一,MessageQueue与Looper一一对应。Handler和Looper可以直接建立联系,从而跟MessageQueue联系起来,具体做法:Handler的构造可以传入Looper,这里传入的是哪个Looper他就运行在对应的线程里,不传入Looper则默认是当前创建handler对象的线程的Looper。
4、MessageQueue是如何分配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; }
首先判断没有target的不要,当前标记为正在使用的不要
加锁,跟next方法取消息动作互斥。
然后判断mQuitting,当Looper quit时会置true,说明handler对应的Looper已经退出了,就不能再入队了。
紧接着标记此条消息正在使用中markInUse,不可做回收、由其他队列入队等操作。
下面就是正式寻找入队位置:
从队列中获取可执行的message:
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; } }
这里插一段looper取消息的代码
for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; }
MessageQueue里没消息Looper怎么办难道是返回null跳出loop循环吗,不是的。没有消息时MessageQueue不是返回null而是阻塞等待消息。代码中可以看出来,MessageQueue返回null是在Looper退出时。
// Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; }
mQuitting在Looper调用quit时通过调用MessageQueue的quit赋值为true的。
没有消息可处理时是通过 nativePollOnce(ptr, nextPollTimeoutMillis); 这里阻塞等待的。nextPollTimeoutMillis是当前最靠前的消息距离现在的时间。
下面说一下取消息的过程:
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()); }
首先一开始有这么一个判断。判断当遇到了一个没有target的message,这个message是一个标志,他标记着队列开始处理异步消息。if中的do-while可以看出是在循环查找下一个异步消息,而且会先执行do,是要跳过取出这个标志message,也就是说一旦打了这个标志将永久存在。
这里说一下异步消息:
首先,异步消息通过一个没有target的消息做标记,当插入这个消息时,标志这从这个消息后开始处理异步消息,从代码可以看出来,每次循环都会先判断标志并找异步消息,找到就执行,没找到就走else直接跳过了,说明有了次标志就只处理异步消息,跳过其他消息了。
那么什么是异步消息呢,他通过FLAG_ASYNCHRONOUS标记为标记。
如何发送一个异步消息呢:
Message有setAsynchronous方法,另外,Handler的构造有将其置为异步的构造,这种异步的handler发送消息时会自动给消息加上异步消息标签。
如何打异步消息标记呢:
postSyncBarrier打标记 removeSyncBarrier去除标记
代码不再粘了,postSyncBarrier就是根据时间找链表位置插入,removeSyncBarrier就是找到这个开始异步消息标志并删除他,最后要执行nativeWake唤醒nativePollOnce,因为一直没处理非异步消息,当前要找一下是不是有非异步消息要处理了。
然后就是取消息动作,没有什么好说的,就是取表头执行就是了。
继续往下看代码,会发现如果本次循环没有取到消息,后边还会有动作,我们会取出IdleHandler循环执行,执行后移除:
// 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);
for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandleridler = 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); } } }
每次取message当发现没有消息了,其实我们还有事情做,执行闲时消息。当IdleHandler都没有了,就会在下次循环时给nativePollOnce设置唤醒时间,阻塞等待。
这个IdleHandler通过MessageQueue的addIdleHandler添加,removeIdleHandler移除。
讲到这里吧,写累了,哈哈。
大家再讨论吧。
- 安卓消息处理机制 【安卓似懂非懂的小知识点】首篇
- 安卓消息处理机制
- 安卓消息处理机制
- 安卓消息处理机制
- 安卓消息处理机制-处理线程
- 安卓的小知识点
- 安卓的消息机制
- 安卓handler消息处理机制
- 13.安卓消息处理机制
- 安卓消息机制
- 关于安卓的小知识点
- 安卓笔记之网络图片查看器小程序以及消息处理机制运用
- 安卓Handler消息机制的例子
- 安卓中的消息机制
- 安卓Handler消息机制
- 安卓 手写消息机制
- 安卓UI线程与异步消息处理机制
- 安卓开发:Handler异步消息处理机制使用
- Codeforces Round #414, rated, Div. 1 + Div. 2 B. Cutting Carrot+【等腰三角形等比例缩小】
- 深度卷积对抗生成网络(DCGAN)
- Windows 7各版本比较
- Qt入门(3)——信号槽
- 2017.5.13--第一天
- 安卓消息处理机制 【安卓似懂非懂的小知识点】首篇
- 《深入理解Java虚拟机——JVM高级特性与最佳实践(第2版)》pdf
- 软件工程-1.概述
- 使用递归求全排列
- codeforces #413 B T-shirt buying(set快速查找)
- Sysbench安装和测试
- Android LauncherModel启动模式解析
- 在服务器上搭建svn并在本地eclipse中使用
- GAN——LSGANs(最小二乘GAN)