解决工做线程更新UI的问题。
因为在Android机制中,为了保证UI操做是线程安全的,规定只容许主线程更新Activity的UI组件。但在实际开发中存在多个线程并发操做UI组件的状况,会致使UI操做线程不安全。故采用Handler机制,当工做线程须要更新UI的时候,经过Handler通知主线程,从而在主线程中更新UI。安全
ps1:为何不用锁呢?用锁会使UI的访问逻辑变得复杂,锁机制会下降UI访问的效率,锁会阻塞某些线程的执行。
UI线程就是APP启动时,就会开启一条ActivityThread线程,称之为主线程。网络
工做线程,则是在操做过程当中,开启的线程,如网络请求线程等。并发
首先看一下该类的注释,就能够知道该类的功能和用处了。
Handler使你能够发送和处理与线程MessageQueue相关联的Message和Runnable。每一个实例都与一个该线程的MessageQueue向关联。当建立Handler后,就会绑定MessageQueue。从绑定以后起,它就能够将Runnable和Message传入消息队列中,并在读取到对应消息时执行他们。less
从描述中,咱们能够提取到几个关键信息。Handler与线程绑定,与线程的MessageQueue绑定,大几率每一个线程就只能有一个MessageQueue。Handler能够发送处理两种类型的数据,Message和Runnable。(这些后面具体描述)异步
Looper是为了为线程提供消息循环。默认状况下,线程没有Looper,可使用prepare()获取循环,并使用loop()方法开始处理信息,只到循环中止。
与信息循环的大部分交互的都是经过Handler类。async
包含Looper要发送的信息列表的低级类。消息不是直接添加到MessageQueue中,而是由与Looper关联的Handler类添加的。函数
从描述中可知,MessageQueue从属于Looper。oop
就是Handler处理的消息。其中有几个比较重要的属性:post
从前文可知,Handler须要绑定MessageQueue,而MessageQueue从属于Looper,因此从建立looper开始。ui
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
咱们能够发现,这里也调用了prepare方法,咱们看一下prepare的方法。
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)); }
这里出现了sThreadLocal字段,这个字段被static final修饰,说明是类共享的常量。(关于ThreadLocal具体讲解能够看上一篇文章)该常量,为每一个线程都提供类Looper的副本。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
ps2 : 如何保证一个线程中只有一个Looper? Looper的构造方法是private,只能在prepare中调用。而且若是一个ThreadLocal获取到相应的value,说明已经建立过。会抛出异常。
ps3:prepare的方法中携带一个布尔类型参数,用于判断是否能够退出循环。子线程的都为true,意味着能够退出;主线程的为false,意味着不能够退出。
接着来看看构造函数,每一个looper对象都会绑定当前线程与一个消息队列
固然就是调用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); 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); } } ...... } }
ps4: 如何退出循环。looper调用quit方法,或者quitSafely方法后,队列就会放回msg == null,这样就能够结束循环。顾名思义,quit就是当即退出,而quitSafely则打上标志,当消息处理彻底以后才结束循环。
ps5:为何这里阻塞了?会不会影响主线程,或者影响CPU呢?这也是一个值得关注的问题,容后面分析。
仍然仍是贴出关键代码
Message next() { ...... 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; } ...... } ...... } }
不难发现nextPollTimeoutMillis是很重要的参数。正常循环时,此参数为0。当有延迟消息,消息没准备好时,则会设置一个延迟时间,让下一次循环执行。当message为空时,此参数会设置成-1。
nativePollOnce方法是一个本地方法,也是阻塞这个消息队列的方法。当前面参数为-1时,就会使消息队列陷入等待状态。
注意这里有个同步方法,锁住类MessageQueue对象。因为处理的消息,是由其余线程传入的,为了保证线程安全,就得Synchronized。
从前文得知Hanler应该能够处理两类数据?为何这里就只有Message呢?后文再说,咱们先看处理方法:
当message有回调时(实际上就是前文的Runnable的类型数据),就会调用这个回调的run方法。
message.callback.run();
当msg.callback为空时,则此处会使用自定的hanlerMessage方法。
以上四步就是Handler机制处理消息的步骤了。那么Handler机制是如何上传Message的呢?
从前文三种处理方法中,咱们就能够复写出三种重写Handler方法。
ps6: Handler要在处理该Handler(通常为主线程)的线程中建立,而后在工做调用。
ps7:这里的Runnable接口,只是做为一种声明,而不是像传统的要开启一个新的线程,切记。
ps8:子线程中能够用MainLooper去建立Handler吗? 子线程中Handler handler = new Handler(Looper.getMainLooper());,此时二者就不在一个线程中。
建立Message对象
Message msg = Message.obtain(); // 实例化消息对象 msg.what = 1; // 消息标识 msg.obj = "AA"; // 消息内容存放
*
ps9: 建立用了不常见的obtain,而不是new,是由于Message自带缓冲池,避免每次都使用new从新分配内存,只有当线程池无对象时,才会new新对象。
对应两种类型数据,亦有两条路径:
post(Runnable r)
boolean enqueueMessage(Message msg, long when) { ...... synchronized (this) { ...... 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; }
sendMessage(Runnable r)
步骤基本同上。
ps10:若是须要调用延迟的话,调用postDelayed方法,最后底层仍是和上面类似。
ps11:若是简单更新UI则直接使用Runnable便可(可能致使线程耦合度高)。如果要传递数据,则使用Message。
致此,怎么使用Handler机制已经讲述完成了。可是Handler机制的使用仍是存在一些问题的,让咱们继续探究。
一、MessageQueue.next()在取出Msg时,若是发现消息A有延迟且时间没到,会阻塞消息队列。
二、若是此时有非延迟的新消息B,会将其加入消息队列, 且处于消息A的前面,而且唤醒阻塞的消息队列。
三、唤醒后会拿出队列头部的消息B,进行处理。而后会继续由于消息A而阻塞。
四、若是达到了消息A延迟的时间,会取出消息A进行处理。
ActivityThread实质是只是App的入口类,而不是真正的线程。
对于线程便是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程,咱们是毫不但愿会被运行一段时间,本身就退出,那么如何保证能一直存活呢?简单作法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出。
至于消耗资源,涉及到epoll机制(到IO的时候在继续讲解吧),该机制会再有数据到达时,才唤醒主线程工做。不然就让主线程休眠。
此点不够清晰,之后详细研究。
解决办法:
队列中的内容(不管Message仍是Runnable)能够要求立刻执行,延迟必定时间执行或者指定某个时刻执行,若是将他们放置在队列头,则表示具备最高有限级别,当即执行。这些函数包括有:sendMessage(), sendMessageAtFrontOfQueue(), sendMessageAtTime(), sendMessageDelayed()以及用于在队列中加入Runnable的post(), postAtFrontOfQueue(), postAtTime(),postDelay()。